Python学习——面向对象

本文深入探讨了Python的面向对象特性,包括类的实例初始化、方法动态性、类方法、静态方法、私有属性和property装饰器的使用,以及继承、多态、组合等核心概念。还介绍了str方法的重写、运算符重载、深拷贝和浅拷贝等实用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面向对象的三大特征

'''
面向过程是对简单任务的处理,着重于解决的手段 是执行者的思维
面向对象是处理复杂、大型任务以及需要协作的任务的方法,将大型任务拆解程小部分,再由面向过程来处理 是设计者的思维
python面向对象的三大特征:
1.封装
2.继承
3.多态
'''

'''
类相当于一个模具,所有定义在类中的函数,都已经包含了类的属性
类里面除了存放属性如年龄、姓名等内容,还会储存方法,方法存储在值中
函数定义在类里就叫方法
'''

initial类的实例属性初始化

class Student:  #构造类 class声明
    def __init__(self,name,score):  #initial初始化类,括号内第一个必须是self,给类赋值
        self.name=name      #转化为对象的属性,再次调用必须用self进行调用
        self.score=score
    #可以通过self进行调用 self后面是定义的实例属性名
    #当需要构造一个类中的对象时,init规定了需要传入的实际参数种类和个数,是构造的方法
    def say_score(self):
        self.age=18         #新增一个age的实例属性
        print("姓名:{0} 成绩:{1}".format(self.name,self.score))

s1 = Student("Jim",85)
s1.say_score()
#Student.say_score(s1) 这是解释器翻译出来的语句
s1.salary=3000
#以上只会对s1这个对象进行修改新创建一个s2不会包含这两项,因为模具没有改变
print(s1.__dict__)
#将s1作为字典输出,s1是一个对象,在类中
s2 = Student("Jack",89)
#print(s2.age) age新增的语句是在say_score中,虽然方法是在模具中但如果不调用不会添加属性
dir(s1) #分析内存调用

print(isinstance(s1,Student))   #判断是否属于哪个类 isinstance(类对象,类名)

call方法——可以像函数那样调用实例属性

class SalaryAccount:
    def __call__(self, salary):
        print(salary*12)
        return salary*12
    #call函数是可以直接调用输入的实例对象的函数,可以直接计算,不需要init初始化
s=SalaryAccount()   #创建实例对象,与call无关,只与初始化init有关
'''
本质上类作为一个对象之所以不可被直接 s()进行调用是因为缺少call函数
def f1():
    pass
f1()

f1()之所以可以直接调用运行正是因为包含了call函数,它定义了他被调用时的动作
'''
s(3000)  #定义了call以后,实例对象可以被看作函数来执行

'''
init定义的初始化是要求创建实例对象时的输入
call是定义了作为可调用对象调用时的输入和程序
'''

类属性

class Members:
    company="996"   #类属性,直接定义在类里面,只会存在在Members的堆内存中,新建的实例对象不会保存一份,而是保留能指向这些属性的指针
    count=0
    def __init__(self,name,age):
        self.name=name  #实例属性,输入以后才会有,每个实例对象中保存的是实例属性
        self.age=age
        Members.count=Members.count+1   #每创建一个对象都会+1,可以计算创建对象的个数

    def say(self):      #类方法,实例对象中的方法会指向类中定义的代码
        print(self,self.name,self.age)

s1=Members("Jim",22)
s1.say()
print(s1.count) #可以调用,但是只存在在类中,独一份

方法的动态性

#Python中方法没有重载,同名方法以后来者为准
class Students:
    def print(self):
        print("1")

def printout(s):    #一定要有形参,即便没有用,因为类方法中需要传入self,需要给他留位置
    print("2")

def print2(s):      #再次新定义一个函数
    print("3")

#方法的添加
Students.printout=printout    #这个语句本质上是新建一个类方法指向同一个函数对象
Students.print=print2   #将原先的类方法指向新的函数对象

s=Students()
s.printout()
s.print()

类方法、静态方法

class Members:
    company="996"   #类属性,直接定义在类里面,等于每个使用磨具的都会被刻上印子
    count=0
    def __init__(self,name,age):
        self.name=name  #实例属性,输入以后才会有
        self.age=age
        Members.count=Members.count+1   #每创建一个对象都会+1,可以计算创建对象的个数

    def say_information(self):  #实例方法,方法跟随模具,但是需要调用才会启动
        print("company:",Members.company)
        print("姓名:{0} 年龄:{1}".format(self.name,self.age))
    #实例方法将代码信息存在类里面,每一个新建的类对象都会包含这一个方法

    @classmethod    #声明类方法,他不会刻在新建的类对象中,仅仅只储存在类模具里,但与类对象有关
    def printout(cls):
        print("over")
        print(cls.count)    #只能调用类属性,用class开头进行调用
    #需要用类名.类方法()进行调用
    # 不可以调用实例属性和实例方法,因为从属关系不同,类方法只存储在类中

    @staticmethod       #他只是存储在类对象中,但是与类无关
    def none(a,b):
        return a+b
    #静态方法也同样只可以调用类属性



s1=Members("jim",22)
s1.say_information()
print(s1.count)
s1.printout()
print(s1.none(1,2))
#之所以实例对象的内存空间并没有储存类属性和静态方法,但它也可以进行调用,这是因为它储存了能指向这些的指针用于引用

私有属性

class Students:
    __school="SHU"
    def __init__(self,name,age):
        self.name=name
        self.__age=age  #私有属性定义是加两个下划线
    def __print(self):
        print("1")
        print(Students.__school)    #类内部可以直接访问私有属性
s=Students("Jim",18)
print(s.name)
print(s._Students__age) #外部不能直接访问私有属性,可以通过 _类名__私有属性名来强制访问
s._Students__print()    #私有方法也是属性,也同样需要这样访问

property——可以应用于外部对私有属性的访问和修改

class Employee:
    def __init__(self,name,salary):
        self.name = name
        self.__salary = salary

    @property
    def salary(self):   #常用于私密属性的访问,给出一个函数,property后可以以属性形式读取
        print("salary printing...")
        print(self.__salary)
    #仅property后面跟的方法会成为property

    @salary.setter
    def salary(self,salary):    #给出一个可以外部更改私密属性的方法,直接属性名赋值即可
        if salary >= 10000:
            self.__salary = salary

s1=Employee("Jim",12000)
s1.salary
s1.salary = 20000   #调用setter重新赋值
s1.salary

'''
property可以把方法转换成为属性,调用直接输出,而原本调用需要有输入形参的(),他是函数
'''

重写str方法——本质上是转字符串类型的返回值修改

class Person:
    def __init__(self,name):
        self.name=name

    def __str__(self):
        '''将对象转换成一个字符串描述'''
        print("重写str方法")
        return "名字是{0}".format(self.name)

s=Person("Jim")
print(s)    #本来返回的是s的信息
a=str(s)    #调用__str__方法,并将return的值返回给a
'''
重写str方法
名字是Jim
重写str方法
print和str()本质上都是调用了类中的__str__方法,他重新定义了返回值
'''

运算符的重新定义

#运算符本质上是调用类中的方法
class Person:
    def __init__(self,name):
        self.name=name

    def __add__(self,other):   #a+b 在解释器中执行是 a.__add__(b),所以进来的是b这个对象,other也是指要加的这个对象
        if isinstance(other,Person):    #判断对象是否是在person类里,只有相同类的实例对象才可以公用方法
            return "{0}-{1}".format(self.name,other.name)   #如果是同类型对象那么init定义也是相同的方法
        else:
            return "不是同类型对象"
p1=Person("Jim")
p2=Person("Jack")
p3="ame"
print(p1+p2)    #p1.__add__(p2)
print(p1+p3)    #会返回不是同类型 因为p3不属于Person类

类的继承

#继承是面向对象的一大特点,使得类有更高的扩展性,也降低了重复工作
class Person:
    def __init__(self,name,age):
        self.name=name
        self.__age=age
    @property
    def say_age(self):
        return self.__age

class Student(Person):      #可以继承多个类,没有特别声明,父类即为object类
    def __init__(self,name,age,score):
        #Person.__init__(self,name,age)  #调用父类的初始化,给解释器明确关系,也可以用super调用
        super(Student,self).__init__(name,age)
        self.score=score
    '''
    子类继承除了构造方法以外的一切成员
    当子类中没有重写init时,会默认使用父类的初始化
    '''

s=Student("Jim",22,85)
print(s.say_age)
#子类里面没有东西,但是继承的父类里有方法,可以调用
print(s.name)
print(s.score)

super()调用父类方法

#super()获得的仅仅是父类定义而非父类对象
class A:
    def say(self):
        print("aa")

class B(A):
    def say(self):
        super(B,self).say() #super(子类名,self).方法名([参数])
        #A.say(self)    也可以获得父类定义 父类名.函数名(self,[参数])
        print("bb")

B().say()

继承与子类方法的重写

class Person:
    def __init__(self,name,age):
        self.name=name
        self.__age=age

    def say_name(self):
        print(self.name)

    def say_age(self):
        print(self.__age)

class Student(Person):
    def __init__(self,name,age,score):
        Person.__init__(self,name,age)  #调用父类的初始化,给解释器明确关系
        self.score=score
    def say_name(self): #子类方法重写父类方法后,子类中不会再调用父类方法,父类方法不会被改变
        print("My name is {0}".format(self.name))

    #子类里面的同名方法已经重写

s1=Student("Jack",23,90)
s1.say_name()
s1.say_age()    #子类继承可以调用父类
#父类不会被改变

print(Student.mro())    #查看类的层次结构
print(dir(s1))  #查看结构属性,方法也是一种属性

多态——建立在继承的基础上

#对于同一个方法,不同的子类有不同的运算
#多态必须存在继承和方法的重写
class man:
    def eating(self):
        print("eating")

class chinese(man):
    def eating(self):
        print("with chopstick")

class indian(man):
    def eating(self):
        print("with hands")

class english(man):
    def eating(self):
        print("with folk & knife")

def man_eating(m):  #传入一个对象
    if isinstance(m,man):   #isinstance()可以查找该对象是否在后面的类中,子类存在父类里
        m.eating()  #对象.eating() 调用不同子类里的方法
    else:
        print("Error")

man_eating(indian())    #用对象来调用

组合

class A1:
    def say_a1(self):
        print("A1,A1")
class B1(A1):       #继承是子母类关系,子类可以调用母类的方法不需要重新定义
    pass

b1=B1()
b1.say_a1()

print(">>>>>>组合测试")
class A2:
    def say_a2(self):
        print("A2,A2")

class B2:
    def __init__(self,c):   #组合是通过将另一个类创建的对象在新的类中初始化
        self.inner=c
#组合可以使得实例对象能够递归调用其他类的方法
a2=A2()
b2=B2(a2)
b2.inner.say_a2()
'''
组合的本质是将具有特殊方法的类作为自己的属性进行初始化,通过 实例对象.属性.特殊方法() 可以调用其他类中的方法
形成代码复用
'''

深拷贝、浅拷贝

import copy
class Person:
    def __init__(self,hands,feet):
        self.hands = hands
        self.feet = feet
    pass

class hands:
    pass

class feet:
    pass

h1=hands()
f1=feet()
P1=Person(h1,f1)    #feet 和 hands是导入的两个子对象,子对象是根据另外两个类创建
P2=copy.copy(P1)    #浅拷贝
print(P1,P1.hands,P1.feet)
print(P2,P2.hands,P2.feet)
'''
<__main__.Person object at 0x0000018EE8CE3310> <__main__.hands object at 0x0000018EE8CE3B80> <__main__.feet object at 0x0000018EE8CE3730>
<__main__.Person object at 0x0000018EE8D3A110> <__main__.hands object at 0x0000018EE8CE3B80> <__main__.feet object at 0x0000018EE8CE3730>
可以看到浅拷贝是创建了一个新的对象指向同样的子对象,不会全部复制一份,只拷贝源对象
'''

print(">>>>>>>测试深拷贝")
P3=copy.deepcopy(P1)
print(P1,P1.hands,P1.feet)
print(P3,P3.hands,P3.feet)
'''
<__main__.Person object at 0x000002745E89BEB0> <__main__.hands object at 0x000002745E89BFD0> <__main__.feet object at 0x000002745E89BEE0>
<__main__.Person object at 0x000002745E899E10> <__main__.hands object at 0x000002745E899CF0> <__main__.feet object at 0x000002745E899C90>
可以看到深拷贝会全盘复制所有的子对象,递归拷贝全部的子对象
'''

类的特殊属性

class A:
    pass

class B:
    pass

class C(B,A):
    pass

c=C()

print(C.mro())  #类的层次结构
print(C.__base__)   #父类
print(C.__bases__)  #直接父类的元组,多重继承
print(c.__class__)  #所属类的信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值