大师兄的Python学习笔记(二): 面向对象和类

大师兄的Python学习笔记(一): 基础篇
大师兄的Python学习笔记(三): 模块和包

三、面向过程和面向对象的思维方式

1.面向过程(Proceduce Oriented)
  • 如果想吃西红柿炒鸡蛋:
    step1: 打鸡蛋
    step2: 切西红柿
    step3: 热锅
    step4: 炒鸡蛋
    step5: 炒西红柿
    step6: 放盐
    step7: 出锅

  • 这种把任务切分成一系列子工作,自顶向下一步一步实现的方式叫做面向过程

2. 面向对象(Object Oriented)
  • 如果想吃西红柿炒鸡蛋:
    step1: 设计一个饭馆的蓝图(类)
    饭馆包含食材:西红柿、鸡蛋
    饭馆包含调料:盐
    饭馆包含炊具:锅
    饭馆包含功能:炒菜
    step2: 根据蓝图建造一个饭馆(对象)
    step3: 点西红柿炒鸡蛋这道菜
  • 这里我们实际上是将制作西红柿炒鸡蛋的过程封装到对象饭馆中。
  • 吃饭的人只需要对厨房下达命令,至于功能的实现方式则不用关心。
  • 这种将功能抽象封装的方式叫做面向对象
3.面向对象的优缺点

1)易复用
建立厨房后,如果为一万人提供西红柿炒鸡蛋,我们只需要下达一万次命令即可,不用重复造轮子。
2)易维护
如果想吃黄瓜炒鸡蛋,只需将食材中的西红柿替换成黄瓜,不用重新设计整个流程。
3)易扩展
如果想吃西红柿鸡蛋盖饭,只需要增加食材大米和蒸饭的功能,而并不需要重建一个厨房。
4)缺点:性能低于面向过程
如果只是一个人吃饭,盖饭馆就很浪费。

4.总结

1)面向过程适合简单、少量的任务。
2)面向对象适合复杂、大量的任务。
3)面向对象并不能取代面向过程,面向对象的最底层是面向过程。

四、类和对象

1.类
  • 类就是具备某些共同特征的实体的集合,它是一种抽象的数据类型,它是对所具有相同特征实体的抽象。
  • 比如:西红柿和鸡蛋都属于食材,食材是个抽象的概念,是一个类。
2.对象
  • 对象是具体的事物,单个的个体。
  • 比如:西红柿和鸡蛋可以是对象、盐和锅可以是对象、西红柿炒鸡蛋可以是对象,厨房也可以是对象。
3.类和对象的关系

类是对象的概括,对象是类的具体体现

五、在Python中实现类和对象

1.类的声明
  • 用class关键字。
  • 类的成员只可以是属性或方法。
  • 属性可以看理解为类中的变量。
  • 方法可以看理解为类中的函数。
class <类名>():
     <类的成员> # 这里必须有缩进
>>>class FoodMaterial():
>>>    price = 10  # 属性
>>>    def cut():   # 方法
>>>        print('我被切成片了!')    
2.类的实例化
  • 也就是将类实例化成为对象
<变量> = <类名>()
>>>class FoodMaterial():
>>>    price = 10  # 属性
>>>    def cut():   # 方法
>>>        print('我被切成片了!')    
>>>tomato = FoodMaterial() # 这里将类实例化后存储在变量中
3. 访问类和对象的成员

1)访问类的属性和调用方法

方法一:
<类名>.<属性名> # 访问属性
<类名>.<方法名>() # 调用方法
方法二:
getattr(<类名>,"<属性名>")
getattr(<类名>,"<方法名>")()
>>>class FoodMaterial():
>>>    price = 10  
>>>    def cut():   
>>>        print('我被切成片了!')    
>>>print(FoodMaterial.price) # 访问属性
>>>10
>>>FoodMaterial.cut() # 调用方法
>>>我被切成片了!

2) 访问对象的属性和方法

方法一:
<对象名>.<属性名> # 访问属性
<对象名>.<方法名>() # 调用方法
方法二:
getattr(<对象名>,"<属性名>")
getattr(<对象名>,"<方法名>")()
>>>class FoodMaterial():
>>>    price = 10  
>>>    def cut(self):   # self我们后面说
>>>        print('我被切成片了!')    
>>>tomato = FoodMaterial() 
>>>print(tomato.price) # 访问属性
>>>10
>>>tomato.cut() # 调用方法
>>>我被切成片了!
4. 修改类和对象的属性

1)修改类的属性

方法一:<类名>.<属性名> = <值> # 修改类属性
方法二:setattr(<类名>,"<属性名>",<值>)
>>>class FoodMaterial():
>>>    price = 10  
>>>tomato = FoodMaterial()
>>>FoodMaterial.price = 20
>>>print(FoodMaterial.price)
20
>>>print(tomato.price) # 这时对象的属性也跟着改变了
20

2)修改对象的属性

  • 如果对象的属性被修改过,就不会跟着类的属性同步了。
方法一:<对象名>.<属性名> = <值> # 修改对象属性
方法二:setattr(<对象名>,"<属性名>",<值>)
>>>class FoodMaterial():
>>>    price = 10  
>>>tomato = FoodMaterial()
>>>tomato.price = 20
>>>print(tomato.price)
20
>>>print(FoodMaterial.price) # 这时类的属性并没有改变
10
>>>FoodMaterial.price = 30 # 如果这时我们再修改类的属性
>>>print(tomato.price) # 对象的属性不会再跟着改变
20
5. 增加类和对象的属性

1)增加类的属性

方法一:<类名>.<属性名> = <值> 
方法二:setattr(<类名>,"<属性名>",<值>)
>>>class FoodMaterial():
>>>    price = 10  
>>>tomato = FoodMaterial()
>>>FoodMaterial.colour = 'red'
>>>print(FoodMaterial.colour)
red
>>>print(tomato.colour) # 这时对象的属性也跟着增加了
red

2)增加对象的属性

方法一:<对象名>.<属性名> = <值> # 增加对象属性
方法二:setattr(<对象名>,"<属性名>",<值>)
>>>class FoodMaterial():
>>>    price = 10  
>>>tomato = FoodMaterial()
>>>tomato.colour = 20
>>>print(tomato.colour)
red
>>>print(FoodMaterial.colour) # 这时类的属性并没有增加
AttributeError: type object 'FoodMaterial' has no attribute 'colour'
>>>FoodMaterial.colour = 'yellow' # 如果这时我们再修改类的属性
>>>print(tomato.colour) # 对象的属性不会改变
red
6. 删除类和对象的属性

1)删除类的属性

方法一:del <类名>.<属性名>  # 删除类属性
方法二:delattr(<类名>,"<属性名>")
>>>class FoodMaterial():
>>>    price = 10  
>>>    colour = 'red'
>>>tomato = FoodMaterial()
>>>del FoodMaterial.price
>>>print(FoodMaterial.price) # 删除属性
AttributeError: type object 'FoodMaterial' has no attribute 'price'
>>>print(tomato.colour) # 这时对象的属性也被删除了
AttributeError: type object 'FoodMaterial' has no attribute 'price'

2)删除对象的属性

  • 从下面的例子就可以看出,对象的属性在没有单独定义的情况下,实际上是调用了类的属性
  • 如果单独为对象创建属性,在属性名相同的情况下,优先调用对象的属性
方法一:del <对象名>.<属性名> # 删除对象属性
方法二:delattr(<对象名>,"<属性名>")
>>>class FoodMaterial():
>>>    price = 10  
>>>    colour = 'red'
>>>tomato = FoodMaterial()
>>>del tomato.price # 这里会提示tomato并没有price属性,实际上是调用的FoodMaterial得price属性
AttributeError: price

>>>tomato.price = 20 # 为对象增加属性
>>>print(tomato.price) # 覆盖了类的属性
20
>>>del tomato.price # 删除了对象的属性
>>>print(tomato.price) # 这里又调用了类的属性
10

>>>del FoodMaterial.price # 如果这时我们将类的属性也删除掉
>>>print(tomato.price) # 无法调用属性,所以报错了
AttributeError: 'FoodMaterial' object has no attribute 'price'
7.关于self
  • self是类和对象中的特一个特殊的参数,代表类本身。
  • self只在类和对象中存在。
  • self不需要赋值。
  • self是类中默认的第一个参数。
  • 其实self并不是系统指定的关键字,他可以叫任何名字,只要他是类中的第一个参数就可以。
    1)在类中使用self参数
  • 很少直接使用类中的self参数
>>>class FoodMaterial():
>>>    def cut(self) :
>>>        print('我被切成片了!')
>>>FoodMaterial.cut(FoodMaterial) # 这时需要将类自身作为参数返回,不然会报错。
我被切成片了!
  • 因为对象也属于类,所以可以用对象作为self参数。但是这种用法比较奇葩,不推荐使用。
>>>class FoodMaterial():
>>>    def cut(self) :
>>>        print('我被切成片了!')
>>>tomato = FoodMaterial()
>>>FoodMaterial.cut(tomato) # 这里将方法当做参数代表类本身,也可以正常运行
我被切成片了!
  • 如果不使用self名称,也没有任何区别,但不推荐这样使用。
>>>class FoodMaterial():
>>>    def cut(somethingbutnotself) : # 证明self并不是关键字
>>>        print('我被切成片了!')
>>>FoodMaterial.cut(FoodMaterial) 
我被切成片了!

2)在对象中使用self参数

  • 在调用对象方法时,会默认将自身作为第一个参数返回。所以如例(3.2)中,如果在没有为方法添加self参数,而直接用对象中的该方法会报错。
>>>class FoodMaterial():
>>>    def cut() :
>>>        print('我被切成片了!')
>>>tomato = FoodMaterial()
>>>tomato.cut() # 这里会提示传入了一个参数,但是cut()不需要传入参数
TypeError: cut() takes 0 positional arguments but 1 was given

>>># 正确的调用方式
>>>class FoodMaterial():
>>>    def cut(self) :
>>>        print('我被切成片了!')
>>>tomato = FoodMaterial()
>>>tomato.cut() # tomato会默认将自身作为第一个参数传入方法
我被切成片了!
  • 在对象中self改变了变量和函数的作用域,成为对象内的全局变量。
>>>class FoodMaterial():
>>>    def __init__(self): # __init__是魔法函数,这里可以先理解为一个在对象创建时自动执行的方法。
>>>        self.price = 10 # 这里self.price成为对象内的全局变量
>>>    def getPrice(self) :
>>>        print('我的价格是:{price}'.format(price=self.price)) # 调用了方法内的变量self.price,证明其作用域是全局
>>>tomato = FoodMaterial() 
>>>tomato.getPrice()
我的价格是:10
8.Python中的构造函数
  • 构造函数是一种特殊的方法,用来在创建对象时为对象赋值。
  • Python中构造函数的方法是: def __init__(self,arg1,arg2)
  • 构造函数只能调用一次,就是在创建类的时候调用
  • __init__ 其实是一个魔法函数,后面会介绍。
>>>class FoodMaterial():
>>>    def __init__(self,price,colour): # 构造函数,参数在创建类时需要提供
>>>          self.price = price 
>>>          self.colour = colour
>>>          self.show() # 在创建类时就要执行的方法
>>>    def show(self):
>>>          print(self.price)
>>>          print(self.colour)
>>>tomato = FoodMaterial(price=10,colour='red') # 这里并没有再专门调用show(),在创建时就自动执行了。
10
red

>>>print(FoodMaterial.price) # 直接用类访问不到构造函数中的成员,因为构造函数只在创建对象时执行。
AttributeError: type object 'FoodMaterial' has no attribute 'price'

六、面向对象的三大特征

  • 封装
  • 继承
  • 多态
1. 封装
  • 封装就是对对象的成员进行访问限制,是为了保护私隐,明确区分内外。
  • 封装的级别分为:公开(public)、受保护的(protected)、私有的(private)
  • 与Java不同,Python中没有public,private,protected的关键字
  • Python中的封装并不是严格的语法,更像是一种规范。

1)公开的(public)
公共的封装实际对成员没有任何操作,任何地方都可以访问。
2)受保护的(protected)

  • 受保护的成员名称前增加一个下划线_。
  • 受保护的封装在类中或者子类中都可以访问,但在外部不允许访问。
  • 我测试受保护的成员实际上是可以直接访问的,应该只是一种书写规范,希望有人可以指正我。
_<受保护的成员名>
>>>class FoodMaterial():
>>>    _price = 10 # 受保护的属性
>>>    def _getPrice(self) : # 受保护的方法
>>>        print('我的价格是:{price}'.format(price=self.price)) 

3)私有的(private)

  • 私有员名称前增加两个划线__。
  • 私有成员只能在当前类或对象中访问。
  • Python中的私有实现方式是一种被称为name mangling的障眼法。
__<私有的成员名>
>>>class FoodMaterial():
>>>    __price = 10 # 私有的属性
>>>    def __getPrice(self) : # 私有的方法
>>>        print('我的价格是:{price}'.format(price=self.__price)) 
>>>tomato = FoodMaterial() # 实例化
>>>print(tomato.__price) # 注意,这里的报错是:不存在这个值!。
AttributeError: 'FoodMaterial' object has no attribute '__price'
>>>tomato.__getPrice()
AttributeError: 'FoodMaterial' object has no attribute '__getPrice'
  • 用魔法函数__dict__查看类的全部成员,可以看出私有成员实际是被改名为:_<类名>__<成员名>
>>>print(FoodMaterial.__dict__)
>>>mappingproxy({'__module__': '__main__',
>>>              '_FoodMaterial__price': 10, # 这里可以看到被改名了
>>>              '_FoodMaterial__getPrice': <function __main__.FoodMaterial.__getPrice(self)>,
>>>              '__dict__': <attribute '__dict__' of 'FoodMaterial' objects>,
>>>              '__weakref__': <attribute '__weakref__' of 'FoodMaterial' objects>,
>>>              '__doc__': None})

>>>print(tomato._FoodMaterial__price) # 试着访问改名后的成员
10
>>>print(tomato._FoodMaterial__getPrice())
我的价格是:10
2. 继承

1)继承的作用:

  • 继承就是一个类可以获得另外一个类中的成员属性和成员方法。
  • 继承可以减少代码,增加代码的复用功。
  • 继承可以建立类与类直接的关系。

2)继承与被继承的概念:

  • 被继承的类叫父类,也叫基类,也叫超类。
  • 用于继承的类,叫子类,也叫派生类。
  • 所有的类全都是object的子类,继承时如果不特别指定父类,将默认继承object类。

3)继承的语法

class <子类名>(<父类名>):
         <类的内容> 
>>># 父类
>>>class Dog(object): # 这里不写object也会默认继承object
>>>    type = 'dog'
>>>    legs = 4
>>>    def bark(self):
>>>         print('汪汪!')

>>># 子类
>>>class Schnauzer(Dog): # schnauzer继承了dog类
>>>     sub_type = 'schnauzer'

4)继承的特性

  • 子类一旦继承父类,则可以使用父类中除私有成员外的所有内容。
  • 子类继承父类后并没有将父类成员完全赋值到子类中,而是通过引用关系访问调用。
  • 子类中可以定义独有的成员属性和方法。
  • 子类中定义的成员和父类成员如果相同,则优先使用子类成员。
>>># 父类
>>>class Dog(object): # 这里不写object也会默认继承object
>>>    type = 'dog'
>>>    def bark(self):
>>>         print('汪汪!')
>>>    def __showLegs(self): # 私有成员不会被继承
>>>         print(4)

>>># 子类
>>>class Schnauzer(Dog): # schnauzer继承了dog类
>>>     sub_type = 'schnauzer'
>>>     def bark(self): # 这里覆盖了父类的方法
>>>          print('嗷嗷1')
>>>qq = Schnauzer()

>>>print(qq.type) # 继承了父类的属性
'dog'

>>>print(qq.bark()) # 同名时首先引用了自己的方法
嗷嗷!

>>>print(qq.sub_type) # 有了自己的属性
'schnauzer'

>>>print(qq.__showLegs()) # 私有变量不会被继承
AttributeError: 'schnauzer' object has no attribute '__showLegs'

5)super函数

  • super()函数用于调用父类的一个方法。
  • super().<父类方法名>() = <父类名>.<父类方法名>()
self(<父类名>,self).<方法名>(参数) # 意思是将父类的方法转换为自己的方法。
self().<方法名>(参数) # python3.0中可以省略self()中的内容,但多重继承中还是需要。
>>># 父类
>>>class Dog(): 
>>>    def bark(self):
>>>         print('汪汪!')

>>># 子类
>>>class Schnauzer(Dog): # schnauzer继承了dog类
>>>    def barkTwice(self): 
>>>         super().bark() # 这里调用了父类的方法
>>>         print('嗷嗷!')
>>>qq = Schnauzer()
>>>qq.barkTwice()
汪汪!
嗷嗷!

6)继承的顺序

  • 优先查找自己的函数
  • 没有则查找父类
  • 如果父类没有则查找父类的父类
>>># 父类
>>>class Dog(): 
>>>    type = 'dog'

>>># 子类
>>>class Schnauzer(Dog): 
>>>    pass

>>># 孙类
>>>class BabySchnauzer(Schnauzer): 
>>>    pass

>>>qq = BabySchnauzer()
>>>print(qq.type) # 继承了祖父类的属性s
'dog'

7)继承构造函数

  • 如果定义了构造函数,则实例化时使用构造函数。
  • 如果没定义,则自动查找父类构造函数。
  • 如果重写了构造函数,还要继承父类的构造方法,可以使用 super()方法。
>>># 父类
>>>class Dog(): 
>>>    def __init__(self,legs):
>>>        self.type = 'dog'
>>>    def fatherType(self):
>>>        print('father type is {type}'.format(type=self.type))

>>># 子类
>>>class Schnauzer(Dog): 
>>>     def __init__(self,legs):
>>>         super().__init__(self,legs) # 这里调用了父类的构造函数,将参数传入
>>>     def sonType(self):
>>>         print('son type is {type}'.format(type=self.type))

>>>qq = Schnauzer(4)
>>>qq.sonType()
son type is dog
>>>qq.fatherType() # 将参数传给父类的方法,并调用方法
father type is dog

8)多继承

  • 多继承就是子类同时继承多个类,方法是:def <子类名>(<父类一>,<父类二>):
  • 如果两个父类的属性相同,则继承写在前面的父类。
  • 可以通过inspect.getmro(<类名>)查看类的MRO列表(继承顺序)
>>># 父类一·
>>>class Human():
>>>    type = 'human'
>>>    def introHuman(self):
>>>          print('i am human!')

>>>class Fish():
>>>    type = 'fish'
>>>    def introFish(self):
>>>          print('i am fish!')

>>>class Fishman(human,fish): # 多个父类
>>>    pass
>>>f = Fishman()

>>>print(f.introHuman()) # 同时继承了两个父类的方法
i am human!
>>>print(f.introFish())
i am fish!

>>>print(f.type) # 属性名相同则继承前面的父类
human

>>>import inspect # 导入inspect包
>>>inspect.getmro(fishman) # 查看fishman类的mro
(__main__.fishman, __main__.human, __main__.fish, object)
  • 单继承和多继承的对比:
/ 单继承 多继承
优点 传承有序逻辑清晰语法简单隐患少 类的功能扩展方便
缺点 功能扩展性受父类限制 继承关系混乱
  • 经典钻石继承问题:当一个孙类同时继承多个父类,而这些父类又继承同一个祖父类时,如果孙类同时继承多个父类的构造函数,祖父类构造函数会被初始化多次。
# 祖父类
>>>class Grandpa(object):
>>>    def __init__(self):
>>>        print('from grandpa')
# 父类1
>>>class Dad1(Grandpa):
>>>    def __init__(self):
>>>        grandpa.__init__(self)
>>>        print('from dad1')
# 父类2
>>>class Dad2(Grandpa):
>>>    def __init__(self):
>>>        grandpa.__init__(self)
>>>        print('from dad2')
# 子类
>>>class Son(Dad1, Dad2):
>>>    def __init__(self):
>>>        dad1.__init__(self) 
>>>        dad2.__init__(self) # 同时调用两个父类的构造函数
>>>        print('from son')

>>>s = Son()
from grandpa
from dad1
from grandpa
from dad2
from son
  • 钻石继承问题解决方案:使用super()函数
# 祖父类
>>>class Grandpa(object):
>>>    def __init__(self):
>>>        print('from grandpa')
# 父类1
>>>class Dad1(Grandpa):
>>>    def __init__(self):
>>>        super().__init__()
>>>        print('from dad1')
# 父类2
>>>class Dad2(Grandpa):
>>>    def __init__(self):
>>>        super().__init__()
>>>        print('from dad2')
# 子类
>>>class Son(dad1, dad2):
>>>    def __init__(self):
>>>        super().__init__() #这里同时继承了两个父类的构造函数
>>>        print('from son')

>>>s = Son() # 这里祖父类只调用了一次
from grandpa
from dad1
from dad2
from son
3. 多态

1)理解多态

  • 首先要理解创建一个类就是创建了一种数据类型,和list,dict是一样的。
>>>a = list()
>>>type(a) 
list
>>>isinstance(a,list) # a 属于list类型
True

>>>class Animal():
>>>    pass
>>>dog = Animal()
>>>type(dog)
__main__.Animal
>>>isinstance(dog,Animal)  # dog是animal数据类型
True
  • 多态是指同一个对象在不同情况下有不同的状态出现。
  • 多态是一种思想,而不是一种语法。
  • 多态增加了程序的灵活性和扩展性。
>>>class Animal(): # 创造动物的数据类型
>>>    pass

>>>class Dog(Animal): # 狗属于动物
>>>    pass

>>>class Cat(Animal): # 猫也属于动物
>>>    pass

>>>qq = Dog()
>>>isinstance(qq,Dog)
True
>>>isinstance(qq,Animal) # qq即属于狗,也属于动物
True

>>>mimi = Cat()
>>>isinstance(mimi,Cat)
True
>>>isinstance(mimi,Animal) # mimi即属于猫,也属于动物
True

2)多态性

  • 指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
  • 多态性是一钟调用方式,
>>>class Animal():
>>>    def talk(self):
>>>        pass

>>>class Human():
>>>    def talk(self):
>>>        print('你好!')

>>>class Dog():
>>>    def talk(self):
>>>        print('汪汪!')

>>>class Cat():
>>>    def talk(self):
>>>        print('喵喵!')

>>> ppl = Human()
>>> pp = Dog()
>>> mimi = Cat()

>>> Human.talk()
你好!
>>> Dog.talk()
汪汪!
>>> Cat.talk()
喵喵!

3)mixin模式

  • mixin模式通过多继承来实现,子类通过继承Mixin类获得一种功能。
  • Mixin类用<类名 + Mixin>的方式起名,但这不是语法,而是告诉后来读代码的人,这是一个Mixin类。
  • 一个Mixin类可以理解为一种功能,且只能是一种功能,如果有多个功能则创建多个Mixin类。
  • Mixin不能依赖于子类的实现
  • 子类及时没有继承这个Mixin类, 也能照样工作,只是缺少了某个功能
>>>class Animal():
>>>    pass

>>>class FlyMixin():  # Mixin类
>>>    def fly(self):
>>>        print('I can fly!')

>>>class Bird(Animal,FlyMixin): # 鸟既是动物,也能飞
>>>    pass

>>>kiki = Bird() # kiki作为bird类,获得了fly能力
>>>kiki.fly()
I can fly!

4)mixin模式的优点

  • 使用Mixin可以在不对类进行任何修改的情况下,扩充功能
  • 可以方便的组织和维护不同功能组件的划分
  • 可以根据需要任意调整功能类的组合
  • 可以避免创建很多新的类,导致类的继承混乱

七.类的成员描述符

(这里如果暂时看不懂请先看下一章魔法函数)

  • 类的成员描述符是为了在类中,对类的成员属性进行相关操作而创建的一种方式,大部分属于数据清洗。
  • 属性有三种操作:get获取属性值、set修改或添加属性、delete删除属性。
  • 实现成员描述符包括三种方法:描述器、属性修饰符、property函数
1. 描述器
  • 一个类只要实现了__get__,__set__,__delete__中任意一个方法,我们就可以叫它描述器
  • 如果仅仅实现了__get__.就是非数据描述器 non-data descriptor
  • 同时实现了__get__,__set__,就是数据描述器 data descriptor

1)__get__
不管是类还是实例去访问,默认都获得的是__get__的返回值,但是,如果中间有任何一次重新赋值,那么,这个实例获得的是新的值(对象),已经和原来的描述器完全脱离了关系。

>>>class Tomato():
>>>   def __get__(self,obj,cls):
>>>       print('here inside __get__ !')

>>>class MyClass():
>>>    t = Tomato() #类或实例默认获得__get__的返回值
>>>m = MyClass()
>>>m.t # 调用了__get__
here inside __get__ !

2)__set__
后期通过实例对描述器进行赋值,那么访问的是__set__,并且永远关联起来。但是如果通过修改类属性的方式复制,那么也会被重新获取新的值(对象)。

>>>class Tomato():
>>>   def __set__(self,obj,cls):
>>>       print('here inside __set__ !')

>>>class MyClass():
>>>    t = Tomato() #类或实例默认获得__get__的返回值
>>>m = MyClass()
>>>m.t = 'hello' # 赋值时调用__set__
here inside __set__ !

3)__delete__
采用del删除属性时,触发__delete__。

>>>class Tomato():
>>>   def __delete__(self,obj):
>>>       print('here inside __delete__ !')

>>>class MyClass():
>>>    t = Tomato()
>>>m = MyClass()
>>>del m.t # 用del删除属性时,触发__delete__
here inside __delete__ !
2. 属性修饰符
  • 属性修饰符包括:__getattribute__()、__getattr__()、__setattr__()、__delattr__()

1)__getattribute__()
调用属性或方法时,会先强制调用 getattribute 方法,然后再传回属性的值或者是 函数(方法)的引用。

>>>class Tomato():
>>>    def __init__(self):
>>>        name = 'tomato'
>>>    def __getattribute__(self, item):
>>>        print('inside __getattribute__!')
>>>t = Tomato()
>>>t.name # 调用属性时触发__getattribute__
inside __getattribute__!

2)__getattr__()
在实例以及对应的__dict__中查找属性失败,那么会调用__getattr__函数

>>>class Tomato():
>>>    def __getattr__(self, item):
>>>        print('inside __getattr__!')
>>>t = Tomato()
>>>t.name # 这里name并不存在,所以触发__getattr__
inside __getattr__!

3)__setattr__()
当为类属性赋值时,调用__setattr__()语句

>>>class Tomato():  
>>>    def __setattr__(self, item,value):
>>>        print('inside __setattr__!')
>>>t = Tomato()
>>>t.name = 'tomato' # 虽然和上个例子看起来相似,但其实这里时因为赋值触发__setattr__
inside __setattr__!

4)__delattr__()
只要使用del语句删除属性,就会调用这个方法

>>>class Tomato():
>>>   def __init__(self):
>>>        self.name = 'tomato'
>>>   def __delattr__(self, item):
>>>        print('inside __delattr__!')
>>>t = Tomato()
>>>del t.name # 用del删除元素时触发__delattr__
inside __delattr__!
3. property函数
  • property() 函数的作用是在新式类中返回属性值。
  • 使用语法 property(<get方法>,<set方法>,<del方法>,<属性描述>)
  • get、set和del方法对应描述器的三个方法。
  • 三个方法的名字可以任意取,但是在property()中要按顺序一一对应。
>>>class Tomato():
>>>    def __init__(self):
>>>        self._name = 'tomato' # 创建protected属性
>>>    def get_name(self): # 创建属性的get方法
>>>        print('here inside get_name')
>>>        return self._name
>>>    def set_name(self, value): # 创建属性的set方法
>>>        print('here inside set_name')
>>>        self._name = value
>>>    def del_name(self): # 创建属性的del方法
>>>        print('here inside del_name')
>>>        del self._name
>>>    name = property(get_name, set_name, del_name, "I'm the 'name' property.") # 为name属性附加property
>>>t = Tomato()

>>>print(t.name) # 触发get_name()
here inside get_name
'tomato'

>>>t.name = 'Tomato' # 触发set_name()
here inside set_name

>>>del t.name # 触发del_name()
here inside del_name

>>>help(t.name) # 触发描述
here inside get_name
  • 用装饰器实现property(如果对装饰器不熟悉可以先跳过):
>>>class Tomato():
>>>    def __init__(self):
>>>        self._name = 'tomato' # 创建protected属性
>>>    @property # 相当于get
>>>    def name(self): # 创建属性的get方法
>>>        print('here inside get_name')
>>>        return self._name
>>>    @name.setter # 相当于set
>>>    def set_name(self, value): # 创建属性的set方法
>>>        print('here inside set_name')
>>>        self._name = value
>>>    @name.deleter # 相当于del
>>>    def del_name(self): # 创建属性的del方法
>>>        print('here inside del_name')
>>>        del self._name
>>>t = Tomato()

>>>print(t.name) # 触发get_name()
here inside get_name
'tomato'

>>>t.name = 'Tomato' # 触发set_name()
here inside set_name

>>>del t.name # 触发del_name()
here inside del_name

>>>help(t.name) # 触发描述
here inside get_name

八.类的内置属性

类包含以下默认内置属性:
1)__dict__:以字典的方式显示类的成员组成

>>>class FoodMaterial():
>>>    def __init__(self):
>>>        self._price = 10 # 受保护的属性
>>>    def getPrice(self) : # 受保护的方法
>>>        print('我的价格是:{price}'.format(price=self.price)) 
>>>print(FoodMaterial.__dict__)
{'__module__': '__main__', '__init__': <function FoodMaterial.__init__ at 0x000001A9534477B8>, 'getPrice': <function FoodMaterial.getPrice at 0x000001A953447840>, '__dict__': <attribute '__dict__' of 'FoodMaterial' objects>, '__weakref__': <attribute '__weakref__' of 'FoodMaterial' objects>, '__doc__': None}

>>>tomato = FoodMaterial()
>>>print(tomato.__dict__)
{'_price': 10}

2)__doc__: 获取类的文档信息

>>>class FoodMaterial():
>>>    '''This is class FoodMaterial'''
>>>    pass
>>>print(FoodMaterial.__doc__)
This is class FoodMaterial

>>>tomato = FoodMaterial()
>>>print(tomato.__doc__)
This is class FoodMaterial

3)__name__:获取类的名称,如果在模块中使用,获取模块的名称

>>>class FoodMaterial():
>>>    pass
>>>print(FoodMaterial.__name__)
'FoodMaterial'

4)__bases__: 获取某个类的所有父类,以元组的方式显示

>>>class Animal():
>>>    pass

>>>class Baby():
>>>    pass

>>>class Dog(Animal):
>>>    pass

>>>class puppy(Dog,Baby):
>>>    pass

>>>print(puppy.__bases___)
(__main__.Dog, __main__.Baby)

九.魔法函数

1)__init__
见:构造函数 - 5.8
2)__str__
当类被打印时,将调用str方法的值。

>>>class Dog(object): # 如果不处理__str__方法
>>>    pass
>>>dog = Dog()
>>>print(dog) 
<__main__.Dog object at 0x000001A953477240>

>>>class Dog(object):
>>>    def __str__(self):
>>>        return 'It is a dog!'
>>>dog = Dog()
>>>print(dog) # 返回了__str__方法的值
It is a dog!

3)__new__

  • 初始化对象,在__init__前执行
  • __new__方法包含一个参数,用于传入要实例化的对象
  • __new__方法必须返回一个值,是实例化后的对象
>>>class Dog(object):
>>>    def __new__(cls):
>>>        print(cls)
>>>        return 1
>>>dog = Dog() # 调用了print(cls)
<class '__main__.Dog'>

>>>print(dog) # 由于返回值是1,所以Dog变成了1
1
>>>type(dog) # 类型也变成了整数
int

4)__call__

  • 定义后可以将对象当作函数使用。
>>>class Dog(object):
>>>    def __call__(self,name):
>>>        print('{name} is running!'.format(name=name))
>>>dog = Dog()
>>>dog('qq') # 调用__call__
qq is running!

5)__len__

  • 在用len()方法时,会调用__len__方法
>>>class Dog(object):
>>>    def __len__(self):
>>>        print('very very long!')
>>>dog = Dog()
>>>len(dog)
very very long!
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-100-b3792c81c856> in <module>
----> 1 len(dog)

TypeError: 'NoneType' object cannot be interpreted as an integer

6)__repr__
函数str() 用于将值转化为适于人阅读的形式,而repr() 转化为供解释器读取的形式,某对象没有适于人阅读的解释形式的话,str() 会返回与repr(),所以print展示的都是str的格式。

>>>class Dog(object):
>>>    def __repr__(self):
>>>        return 'It is a dog!'
>>>dog = Dog()
>>>print(dog) # 返回了__str__方法的值
It is a dog!

7)__setattr__
见:类的成员描述符 - 7
8)__getattr__
见:类的成员描述符 - 7
9)__delattr__
见:类的成员描述符 - 7
10)__getattribute__
见:类的成员描述符 - 7
11)__del__
见:类的成员描述符 - 7
12)__getitem__
定义__getitem__方法,就可以用对象像字典一样取值:key[value]

>>>class Dog(object):
>>>    def __init__(self):
>>>        self.name = 'dog'
>>>    def __getitem__(self,key):
>>>        print('here in __getitem__')
>>>        return self.name
>>>dog = Dog()
>>>print(dog['name']) # 调用了__getitem__方法
here in __getitem__
'dog'

13)__delitem__
定义__delitem__方法,就可以用对象像字典一样删除值:del key[value]

>>>class Dog(object):
>>>    def __init__(self):
>>>        self.name = 'dog'
>>>    def __delitem__(self,key):
>>>        print('here in __delitem__')
>>>        del self.name
>>>dog = Dog()
>>>del dog['name'] # 调用了__delitem__方法
here in __delitem__

>>>print(dog.name)
AttributeError: 'Dog' object has no attribute 'name'

十.抽象类

  • 抽象类就是没有具体实现内容的方法成为抽象类
  • 抽象类的主要意义是规范了子类的行为和接口
  • 抽象类的使用需要借助abc模块
  • 抽象类不允许直接实例化
  • 必须继承才可以使用,且继承的子类必须实现所有继承来的抽象方法
  • 假定子类没有是现实所有继承的抽象方法,则子类也不能实例化
>>>import abc # 导入abc模块
>>>class Animal(metaclass=abc.ABCMeta):
>>>    type = animal
>>>    @abc.abstractmethod # 定义抽象方法,无需实现功能
>>>    def bart(self):
>>>        pass
>>>dog = Animal() # 抽象类不能被实例化
TypeError: Can't instantiate abstract class animal with abstract methods bart

>>>class Dog(animal):  # 通过继承实现抽象类
>>>    def bart(self):
>>>        print('嗷嗷!')
>>>dog = Dog()
>>>dog.bart()
嗷嗷!

参考资料



本文作者:大师兄(superkmi)


梦幻微武侠
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容