zoukankan      html  css  js  c++  java
  • Python学习(十四)—— 高级面向对象程序设计

    isinstance(obj,cls)和 issubclass(sub,super)

    isinstance(obj,cls)检查对象obj是否是类cls的对象

    issubclass(sub,super)检查类sub是否是类super的派生类

     1 #isinstance
     2 class Foo:
     3     pass
     4 f = Foo()
     5 print(isinstance(f,Foo))
     6 # True
     7 
     8 #issubclass
     9 class Bar(Foo):
    10     pass
    11 print(issubclass(Bar,Foo))
    12 # True
    isinstance&issubclass
    1 class Foo:
    2     pass
    3 class Bar(Foo):
    4     pass
    5 b1 = Bar()
    6 print(isinstance(b1,Foo))
    7 # True
    8 print(type(b1))
    9 # <class '__main__.Bar'>
    继承的种类关系

    反射

    什么是反射?

    反射的概念有Smith在1982年首次提出,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(反省)。这一概念首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

    四个可以实现自省的函数

    下列方法适用于类和对象

    判断是否包含某个数据属性或函数属性,没有则返回False

     1 class Agency:
     2     def __init__(self,name,addr):
     3         self.name =  name
     4         self.addr = addr
     5 
     6     def sell(self):
     7         print("%s is selling an apartment"%self.name)
     8 
     9     def rent(self):
    10         print("%s is renting an apartment" % self.name)
    11 
    12 a1 = Agency("Lianjia","downstairs")
    13 print(a1.__dict__)
    14 # {'name': 'Lianjia', 'addr': 'downstairs'}
    15 print(hasattr(a1,"name"))
    16 print(hasattr(a1,"sell"))
    17 # True
    18 # True
    hasattr

    查找包含的某个数据属性或函数属性,没有则报错,Default = None

    1 print(getattr(a1,"name"))
    2 print(getattr(a1,"sell"))
    3 func = getattr(a1,"sell")
    4 func()
    5 # Lianjia
    6 # <bound method Agency.sell of <__main__.Agency object at 0x000001D350437C50>>
    7 # Lianjia is selling an apartment
    8 print(getattr(a1,"buy"))
    9 # Wrong
    getattr

    添加或修改某个数据属性或函数属性

    1 setattr(a1,"name","Jinxiu")
    2 print(getattr(a1,"name"))
    3 # Jinxiu
    4 setattr(a1,"run",True)
    5 print(a1.__dict__)
    6 # {'name': 'Jinxiu', 'addr': 'downstairs', 'run': True}
    setattr

    删除某个数据属性或函数属性

    1 delattr(a1,"run")
    2 print(a1.__dict__)
    3 # {'name': 'Jinxiu', 'addr': 'downstairs'}
    delattr

     为什么需要使用反射?

    可以事先定义好接口,接口只在被完成后才会真正执行,这实现了即插即用,其实是一种“后期绑定”。即可以事先将主要逻辑写好(只定义接口),然后后期再去实现接口的功能。

    动态导入模块

    调到顶层模块

    1 module_m = __import__("m.test")
    2 print(module_m)
    3 # <module 'm' (namespace)> #不管多少层,都返回最顶层模块
    4 module_m.test1()
    5 # Wrong
    6 module_m.test.test1()
    7 # test1

    调到所需模块

    1 import importlib
    2 m = importlib.import_module("m.test")
    3 print(m)
    4 # <module 'm.test' from 'D:\PythonStudy\Python全栈\day26\m\test.py'>

    类的内置attr属性

     1 class Foo:
     2     x = 10
     3     def __init__(self,y):
     4         self.y = y
     5     def __getattr__(self,item):
     6         print("getattr is running")
     7 
     8 f = Foo(5)
     9 print(f.x)
    10 print(f.y)
    11 f.selqengelegngq
    12 # 10
    13 # 5
    14 # getattr is running
    getattr
     1 #删除会触发delattr
     2 class Foo:
     3     x = 10
     4     def __init__(self,y):
     5         self.y = y
     6     def __delattr__(self,item):
     7         print("delattr is running")
     8 
     9 f = Foo(5)
    10 del f.x
    11 print(f.__dict__)
    12 # delattr is running
    13 # {'y': 5}
    14 del f.y
    15 # delattr is running
    delattr
     1 # 只要设置属性就会触发,死循环
     2 class Foo:
     3     x = 10
     4     def __init__(self,y):
     5         self.y = y
     6     def __setattr__(self,key,value):
     7         # self.key = value 只要设置就会触发,死循环
     8         self.__dict__[key] = value #新增不会触发
     9         print("setattr is running")
    10 f1 = Foo(5)
    11 print(f1.__dict__)
    12 # setattr is running
    13 # {'y': 5}
    14 f1.z= 8
    15 print(f1.__dict__)
    16 # setattr is running
    17 # {'y': 5, 'z': 8}
    setattr

    这些属性都是系统内置的,自己定义后会使用自己定义的函数, 否则就使用内置的函数

     二次加工标准类型(包装)

    包装:Python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生只是(其他的标准类型均可以通过下面的方式进行二次加工)

    1 class List(list):
    2     pass
    3 l1 = list("Hello World!")
    4 print(l1,type(l1))
    5 l1 = List("Hello World!")
    6 print(l1,type(l1))
    7 # ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'] <class 'list'>
    8 # ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'] <class '__main__.List'>
    继承的种类和原种类
     1 class List(list):
     2     def append(self,p_obj):
     3         if type(p_obj) is str:
     4             super().append(p_obj) #list.append(self,p_obj)
     5         else:
     6             print("Only string input is tactic")
     7 
     8 l1 = List("Hello World!")
     9 l1.append("wewn")
    10 print(l1)
    11 # ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', 'wewn']
    包装定制自己的数据类型

    授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建、修改或删除原有产品的功能。其他的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

    实现授权的关键点就是覆盖__getattr__方法

     1 class FileHandle():
     2     def __init__(self,filename,mode="r",encoding ="utf-8"):
     3         self.file = open(filename,mode,encoding=encoding)
     4         self.mode = mode
     5         self.encoding = encoding
     6     def __getattr__(self,item):
     7         return getattr(self.file,item)
     8 
     9 f1 = FileHandle("a.txt","w+")
    10 f1.write("11111
    ")
    11 print(f1.__dict__)
    12 print("==>",f1.read) #触发__getattr__
    13 # {'file': <_io.TextIOWrapper name='a.txt' mode='w+' encoding='utf-8'>, 'mode': 'w+', 'encoding': 'utf-8'}
    14 # ==> <built-in method read of _io.TextIOWrapper object at 0x00000145B83DC708>
    15 
    16 f1.write("abc
    ")
    17 f1.seek(0)
    18 print(f1.read())
    19 # 11111
    20 # abc
    文件
     1 import time
     2 class FileHandle():
     3     def __init__(self,filename,mode="r",encoding ="utf-8"):
     4         self.file = open(filename,mode,encoding=encoding)
     5         self.mode = mode
     6         self.encoding = encoding
     7     def __getattr__(self,item):
     8         return getattr(self.file,item)
     9     def write(self,item):
    10         t = time.strftime("%Y-%m-%d %X")
    11         self.file.write("%s %s"%(t,item))
    12 
    13 f1 = FileHandle("a.txt","w+")
    14 f1.write("11111
    ")
    15 f1.write("x
    ")
    16 f1.write("y
    ")
    17 f1.seek(0)
    18 print(f1.read())
    19 # 2020-03-13 20:56:30 11111
    20 # 2020-03-13 20:56:30 x
    21 # 2020-03-13 20:56:30 y
    自己定制的文件处理

     

    __getattribute__

    优先级高于__getattr__

     1 class Foo:
     2     def __init__(self,x):
     3         self.x = x
     4     def __getattr__(self,item):
     5         print("getattr is running")
     6     def __getattribute__(self,item):
     7         print("getattribute is running")
     8 f1 = Foo(1)
     9 f1.x
    10 # getattribute is running
    11 f1.y
    12 # getattribute is running
    无论属性是否存在都运行getattribute

    与raise AttributeError 结合使用,可以给getattr传递信息(即调用getattr)

     1 class Foo:
     2     def __init__(self,x):
     3         self.x = x
     4     def __getattr__(self,item):
     5         print("getattr is running")
     6     def __getattribute__(self,item):
     7         print("getattribute is running")
     8         raise AttributeError("Not Existed")
     9 f1 = Foo(1)
    10 f1.x
    11 # getattribute is running
    12 # getattr is running
    13 f1.y
    14 # getattribute is running
    15 # getattr is running
    getattribute与raise异常处理

    描述符  

     1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
    __get__():调用一个属性时,触发
    __set__():为一个属性赋值时,触发
    __delete__():采用del删除属性时,触发

    1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
    2     def __get__(self, instance, owner):
    3         pass
    4     def __set__(self, instance, value):
    5         pass
    6     def __delete__(self, instance):
    7         pass
    定义

    2 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

     1 class Foo:
     2     def __get__(self, instance, owner):
     3         print('触发get')
     4     def __set__(self, instance, value):
     5         print('触发set')
     6     def __delete__(self, instance):
     7         print('触发delete')
     8 
     9 #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
    10 f1=Foo()
    11 f1.name='egon'
    12 f1.name
    13 del f1.name
    描述符类产生的实例进行属性操作并不会触发三个方法的执行
     1 #描述符Str
     2 class Str:
     3     def __get__(self, instance, owner):
     4         print('Str调用')
     5     def __set__(self, instance, value):
     6         print('Str设置...')
     7     def __delete__(self, instance):
     8         print('Str删除...')
     9 
    10 #描述符Int
    11 class Int:
    12     def __get__(self, instance, owner):
    13         print('Int调用')
    14     def __set__(self, instance, value):
    15         print('Int设置...')
    16     def __delete__(self, instance):
    17         print('Int删除...')
    18 
    19 class People:
    20     name=Str()
    21     age=Int()
    22     def __init__(self,name,age): #name被Str类代理,age被Int类代理,
    23         self.name=name
    24         self.age=age
    25 
    26 #何地?:定义成另外一个类的类属性
    27 
    28 #何时?:且看下列演示
    29 
    30 p1=People('alex',18)
    31 # Str设置...
    32 # Int设置...
    33 
    34 #描述符Str的使用
    35 p1.name
    36 p1.name='egon'
    37 del p1.name
    38 # Str调用
    39 # Str设置...
    40 # Str删除...
    41 
    42 #描述符Int的使用
    43 p1.age
    44 p1.age=18
    45 del p1.age
    46 # Int调用
    47 # Int设置...
    48 # Int删除...
    49 
    50 #我们来瞅瞅到底发生了什么
    51 print(p1.__dict__)
    52 print(People.__dict__)
    53 # {}
    54 # {'__module__': '__main__', 'name': <__main__.Str object at 0x0000015757DC79E8>, 'age': <__main__.Int object at 0x0000015757DB1E80>, '__init__': <function People.__init__ at 0x0000015766F2A7B8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
    55 
    56 #补充
    57 print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
    58 print(type(p1).__dict__ == People.__dict__)
    59 # True
    60 # True
    61 #疑问:何时,何地,会触发这三个方法的执行
    运用场景

    3 描述符分两种
      —— 数据描述符:至少实现了__get__()和__set__()

    1 class Foo:
    2     def __set__(self, instance, value):
    3          print('set')
    4     def __get__(self, instance, owner):
    5          print('get')

      —— 非数据描述符:没有实现__set__()

    1 class Foo: 
    2     def __get__(self, instance, owner):
    3         print('get')

    4 注意事项:
    一、描述符本身应该定义成新式类,被代理的类也应该是新式类
    二、必须把描述符定义成这个类的类属性,不能为定义到构造函数中
    三、要严格遵循该优先级,优先级由高到底分别是
      1.类属性

     1 #描述符Str
     2 class Str:
     3     def __get__(self, instance, owner):
     4         print('Str调用')
     5     def __set__(self, instance, value):
     6         print('Str设置...')
     7     def __delete__(self, instance):
     8         print('Str删除...')
     9 
    10 class People:
    11     name=Str()
    12     def __init__(self,name,age): #name被Str类代理,age被Int类代理,
    13         self.name=name
    14         self.age=age
    15 
    16 
    17 #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
    18 
    19 #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错
    20 People.name #恩,调用类属性name,本质就是在调用描述符Str,触发了__get__()
    21 
    22 People.name='egon' #那赋值呢,我去,并没有触发__set__()
    23 del People.name #赶紧试试del,我去,也没有触发__delete__()
    24 # Str调用
    25 #结论:描述符对类没有作用-------->错
    26 
    27 '''
    28 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
    29 People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()
    30 
    31 People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
    32 del People.name #同上
    33 '''
    类属性>数据描述符


      2.数据描述符

     1 #描述符Str
     2 class Str:
     3     def __get__(self, instance, owner):
     4         print('Str调用')
     5     def __set__(self, instance, value):
     6         print('Str设置...')
     7     def __delete__(self, instance):
     8         print('Str删除...')
     9 
    10 class People:
    11     name=Str()
    12     def __init__(self,name,age): #name被Str类代理,age被Int类代理,
    13         self.name=name
    14         self.age=age
    15 
    16 
    17 p1=People('egon',18)
    18 
    19 #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
    20 p1.name='egonnnnnn'
    21 p1.name
    22 print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
    23 del p1.name
    24 # Str设置...
    25 # Str设置...
    26 # Str调用
    27 # {'age': 18}
    28 # Str删除...
    数据描述符>实例属性

      3.实例属性

     1 class Foo:
     2     def func(self):
     3         print('我胡汉三又回来了')
     4 f1=Foo()
     5 f1.func() #调用类的方法,也可以说是调用非数据描述符
     6 #函数是一个非数据描述符对象(一切皆对象么)
     7 print(dir(Foo.func))
     8 print(hasattr(Foo.func,'__set__'))
     9 print(hasattr(Foo.func,'__get__'))
    10 print(hasattr(Foo.func,'__delete__'))
    11 # 我胡汉三又回来了
    12 # ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
    13 # False
    14 # True
    15 # False
    16 #有人可能会问,描述符不都是类么,函数怎么算也应该是一个对象啊,怎么就是描述符了
    17 #笨蛋哥,描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么
    18 #函数就是一个由非描述符类实例化得到的对象
    19 #没错,字符串也一样
    20 
    21 f1.func='这是实例属性啊'
    22 print(f1.func)
    23 #这是实例属性啊
    24 del f1.func #删掉了非数据
    25 f1.func()
    26 # 我胡汉三又回来了
    实例属性>非数据描述符
     1 class Foo:
     2     def __set__(self, instance, value):
     3         print('set')
     4     def __get__(self, instance, owner):
     5         print('get')
     6 class Room:
     7     name=Foo()
     8     def __init__(self,name,width,length):
     9         self.name=name
    10         self.width=width
    11         self.length=length
    12 
    13 
    14 #name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级
    15 #对实例的属性操作,触发的都是描述符的
    16 r1=Room('厕所',1,1)
    17 r1.name
    18 r1.name='厨房'
    19 
    20 class Foo:
    21     def __get__(self, instance, owner):
    22         print('get')
    23 class Room:
    24     name=Foo()
    25     def __init__(self,name,width,length):
    26         self.name=name
    27         self.width=width
    28         self.length=length
    29 
    30 
    31 #name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
    32 #对实例的属性操作,触发的都是实例自己的
    33 r1=Room('厕所',1,1)
    34 r1.name
    35 r1.name='厨房'
    36 # set
    37 # get
    38 # set
    实例属性>非数据描述符2

      4.非数据描述符

     1 class Foo:
     2     def func(self):
     3         print('我胡汉三又回来了')
     4 
     5     def __getattr__(self, item):
     6         print('找不到了当然是来找我啦',item)
     7 f1=Foo()
     8 
     9 f1.xxxxxxxxxxx
    10 # 找不到了当然是来找我啦 xxxxxxxxxxx
    非数据描述符>找不到

      5.找不到的属性触发__getattr__()

    5 描述符使用

    众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制

     1 #描述符进行类型检测
     2 class Typed:
     3     def __init__(self,key):
     4         self.key  = key
     5     def __get__(self,instance,owner):
     6         print("get method")
     7         # print("instance参数[%s]"%instance)
     8         # print("owner参数[%s]" %owner)
     9         return instance.__dict__[self.key]
    10     def __set__(self, instance, value):
    11         print("set method")
    12         # print("instance参数[%s]" % instance)
    13         # print("value参数[%s]" % value)
    14         if not isinstance(value,str):
    15             # print("请传入字符串")
    16             # return
    17             raise TypeError("你传入的不是字符串")
    18         instance.__dict__[self.key] = value
    19     def __delete__(self,instance):
    20         print("delete method")
    21         # print("instance参数[%s]" % instance)
    22         instance.__dict__.pop(self.key)
    23 
    24 
    25 class People:
    26     name = Typed("name") #触发了Typed的set
    27     def __init__(self,name,age,salary):
    28         self.name = name #触发set
    29         self.age = age
    30         self.salary = salary
    31 
    32 p1 = People("Jenny",20,30.5)
    33 p1.name
    34 print(p1.name)
    35 # set method
    36 # get method
    37 # get method
    38 # Jenny
    39 print(p1.__dict__)
    40 p1.name  = "ALLY"
    41 print(p1.__dict__)
    42 # {'name': 'Jenny', 'age': 20, 'salary': 30.5}
    43 # set method
    44 # {'name': 'ALLY', 'age': 20, 'salary': 30.5}
    45 del p1.name
    46 print(p1.__dict__)
    47 # delete method
    48 # {'age': 20, 'salary': 30.5}
    49 p1.name = 18
    50 print(p1.__dict__)
    51 # # set method
    52 # # 请传入字符串
    53 # # {'age': 20, 'salary': 30.5}
    54 # set method
    55 #     raise TypeError("你传入的不是字符串")
    56 # TypeError: 你传入的不是字符串
    57 
    58 #没有类型检测
    59 # def test(x):
    60 #     print("===>",x)
    61 # test("ally")
    62 # test(1)
    63 # # ===> ally
    64 # # ===> 1
    判断name 的str类

    大刀阔斧之后我们已然能实现功能了,但是问题是,如果我们的类有很多属性,你仍然采用在定义一堆类属性的方式去实现

    也可以一步到位

     1 class Typed:
     2     def __init__(self,key,expected_type):
     3         self.key  = key
     4         self.expected_type = expected_type
     5     def __get__(self,instance,owner):
     6         print("get method")
     7         # print("instance参数[%s]"%instance)
     8         # print("owner参数[%s]" %owner)
     9         return instance.__dict__[self.key]
    10     def __set__(self, instance, value):
    11         print("set method")
    12         # print("instance参数[%s]" % instance)
    13         # print("value参数[%s]" % value)
    14         if not isinstance(value,self.expected_type):
    15             # print("请传入字符串")
    16             # return
    17             raise TypeError("你传入的不是",self.expected_type)
    18         instance.__dict__[self.key] = value
    19     def __delete__(self,instance):
    20         print("delete method")
    21         # print("instance参数[%s]" % instance)
    22         instance.__dict__.pop(self.key)
    23 
    24 
    25 class People:
    26     name = Typed("name",str) #触发了Typed的set
    27     age = Typed("age", int)
    28     salary = Typed("salary", float)
    29     def __init__(self,name,age,salary):
    30         self.name = name #触发set
    31         self.age = age
    32         self.salary = salary
    33 
    34 p1 = People("Jenny",20,30.5)
    35 # p2 = People('Arron',"twenty",15)
    36 p3 = People("Fahrenheit",30,"40")
    一步到位判断

     与装饰器结合使用

     1 class Typed:
     2     def __init__(self,key,expected_type):
     3         self.key  = key
     4         self.expected_type = expected_type
     5     def __get__(self,instance,owner):
     6         return instance.__dict__[self.key]
     7     def __set__(self, instance, value):
     8         if not isinstance(value,self.expected_type):
     9             # print("请传入字符串")
    10             # return
    11             raise TypeError("%s你传入的不是%s"%(self.key,self.expected_type))
    12         instance.__dict__[self.key] = value
    13     def __delete__(self,instance):
    14         instance.__dict__.pop(self.key)
    15 
    16 def deco(**kwargs):
    17     def wrapper(obj):
    18         for key,value in kwargs.items():
    19             setattr(obj,key,Typed(key,value))
    20         return obj
    21     return wrapper
    22 
    23 @deco(name=str,age=int,gender=str,salary=float)
    24 class People:
    25     def __init__(self,name,age,gender,salary):
    26         self.name = name #触发set
    27         self.age = age
    28         self.gender = gender
    29         self.salary = salary
    30 
    31 p1 = People("Jenny",20,"female",90)
    32 print(People.__dict__)
    33 p2 = People('Arron',"twenty","male",500)
    装饰器+描述符

    6 描述符总结

    描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

    描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

    7 利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

     1 class Lazyproperty:
     2     def __init__(self,func):
     3         print("------",func)
     4         self.func = func
     5 
     6 class Room:
     7     def __init__(self,name,width,length):
     8         self.name = name
     9         self.width = width
    10         self.length = length
    11     @Lazyproperty #area = Lazyproperty(area)
    12     def area(self):
    13         return self.width*self.length
    14 r1 = Room("Bedroom",3,5)
    15 # ------ <function Room.area at 0x000001B7043AA598>
    简单的property
     1 class Lazyproperty:
     2     def __init__(self,func):
     3         print("------",func)
     4         self.func = func
     5     def __get__(self,instance,owner):
     6         print("get")
     7         res = self.func(instance)
     8         return res
     9 class Room:
    10     def __init__(self,name,width,length):
    11         self.name = name
    12         self.width = width
    13         self.length = length
    14 
    15     @Lazyproperty #area = Lazyproperty(area)
    16     def area(self):
    17         return self.width*self.length
    18 r1 = Room("Bedroom",3,5)
    19 print(r1.__dict__)
    20 print(r1.area)
    21 # ------ <function Room.area at 0x000001DA1DA5A620>
    22 # {'name': 'Bedroom', 'width': 3, 'length': 5}
    23 # get
    24 # 15
    自定义property求面积
     1 class Lazyproperty:
     2     def __init__(self,func):
     3         self.func = func
     4     def __get__(self,instance,owner):
     5         if instance is None:
     6             return self
     7         res = self.func(instance)
     8         setattr(instance,self.func.__name__,res)
     9         return res
    10 class Room:
    11     def __init__(self,name,width,length):
    12         self.name = name
    13         self.width = width
    14         self.length = length
    15     @Lazyproperty #area = Lazyproperty(area)
    16     def area(self):
    17         return self.width*self.length
    18 r1 = Room("Bedroom",3,5)
    19 print(r1.__dict__)
    20 # {'name': 'Bedroom', 'width': 3, 'length': 5}
    21 print(r1.area)
    22 # 15
    23 print(r1.__dict__)
    24 # {'name': 'Bedroom', 'width': 3, 'length': 5, 'area': 15}
    延迟计算

    8 利用描述符原理完成一个自定制@classmethod

    9 利用描述符原理完成一个自定制的@staticmethod

    __enter__和__exit__ 

    在操作文件时可以用

    1 with open("a.txt") as f:
    2    "代码块"

    上述叫做上下文管理协议,即with语句,为了让一个对象兼容

    with obj ==>触发obj.__enter__(),拿到返回值

    as f ===>f = 返回值

    with obj as f <===>f = obj.__enter__()

    执行代码块:
    1、无异常,整个代码块运行完毕后去触发__exit__,它的三个参数都为None

    2、有异常,从异常出现的位置直接触发__exit__

      a.如果__exit__的返回值为True,代表吞掉异常

      b.如果__exit__的返回值不为True,代表吐出异常

      c.__exit__运行完毕就代表了整个with语句执行完毕

     1 class Open:
     2     def __init__(self,name):
     3         self.name = name
     4     def __enter__(self):
     5         print("enter is running")
     6         return self
     7     def __exit__(self,exc_type,exc_val,exc_tb):
     8         print("exit is running")
     9 # f1 = Open("a.txt") 不能这样使用
    10 with Open("a.txt") as f: #触发enter,拿到enter return的结果
    11     print(f.name)
    12     print("====>")
    13     print("====>")
    14     print("====>")
    15 
    16 print("*******")
    17 # enter is running #enter在打开时运行
    18 # a.txt
    19 # ====>
    20 # ====>
    21 # ====>
    22 # exit is running #exit在文件运行后运行
    23 # *******
    上下文协议-enter
     1 class Open:
     2     def __init__(self,name):
     3         self.name = name
     4     def __enter__(self):
     5         print("enter is running")
     6         return self
     7     def __exit__(self,exc_type,exc_val,exc_tb):
     8         print("exit is running")
     9         print(exc_type)
    10         print(exc_val)
    11         print(exc_tb)
    12 # f1 = Open("a.txt") 不能这样使用
    13 with Open("a.txt") as f: #触发enter,拿到enter return的结果
    14     print(f.name)
    15     print(f.x)
    16     print(f)
    17 # exit的三个信息在触发异常时分别代表异常的种类、值和traceback信息
    18 # exit的三个信息在无异常时返回None
    exit

    加上return True就可以不报错

     1 class Open:
     2     def __init__(self,name):
     3         self.name = name
     4     def __enter__(self):
     5         print("enter is running")
     6         return self
     7     def __exit__(self,exc_type,exc_val,exc_tb):
     8         print("exit is running")
     9         print(exc_type)
    10         print(exc_val)
    11         print(exc_tb)
    12         return True #这样就不会报错了
    13 # f1 = Open("a.txt") 不能这样使用
    14 with Open("a.txt") as f: #触发enter,拿到enter return的结果
    15     print(f.name)
    16     print(f.x) #触发exit函数了
    17     print(f) #exit执行完毕就结束了,不会继续运行with语句了
    18 # enter is running
    19 # a.txt
    20 # exit is running
    21 # <class 'AttributeError'>
    22 # 'Open' object has no attribute 'x'
    23 # <traceback object at 0x000001CCFCC810C8>
    exit+return True

    用途:

    1、使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无需手动

    2、在需要管理一些资源比如文件、网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无需再去关注该问题,大有用处

    类的装饰器 

      回顾一下函数装饰器

     1 #函数装饰器
     2 def deco(func):
     3     print("=====")
     4     return func
     5 @deco #fun = deco(fun)
     6 def fun():
     7     print("*****")
     8 # =====
     9 fun()
    10 # *****

    类的装饰器

     1 #类装饰器
     2 def deco(obj):
     3     print("=====")
     4     return obj
     5 
     6 @deco #Foo = deco(Foo)
     7 class Foo:
     8     print("$$$$$")
     9 f1 = Foo()
    10 print(f1)
    11 # $$$$$
    12 # =====
    13 # <__main__.Foo object at 0x0000024FE9D052B0>

    装饰器增强版

     1 def typed(**kwargs):
     2     def deco(obj):
     3         for key,value in kwargs.items():
     4             setattr(obj,key,value)
     5         return obj
     6     return deco
     7 
     8 @typed(x = 1, y = 2, z= 3) #1.typed(x = 1, y = 2, z= 3) 2.@deco Foo=deco(foo)
     9 class Foo:
    10     pass
    11 print(Foo.x,Foo.y,Foo.z)
    12 # 1 2 3
    13 
    14 @typed(name = "alex")
    15 class Bar:
    16     pass
    17 print(Bar.name)
    18 # alex
    19 
    20 装饰器增强
    装饰器增强

    回到描述器应用

     1 class Typed:
     2     def __init__(self,key,expected_type):
     3         self.key  = key
     4         self.expected_type = expected_type
     5     def __get__(self,instance,owner):
     6         return instance.__dict__[self.key]
     7     def __set__(self, instance, value):
     8         if not isinstance(value,self.expected_type):
     9             # print("请传入字符串")
    10             # return
    11             raise TypeError("%s你传入的不是%s"%(self.key,self.expected_type))
    12         instance.__dict__[self.key] = value
    13     def __delete__(self,instance):
    14         instance.__dict__.pop(self.key)
    15 
    16 def deco(**kwargs):
    17     def wrapper(obj):
    18         for key,value in kwargs.items():
    19             setattr(obj,key,Typed(key,value))
    20         return obj
    21     return wrapper
    22 
    23 @deco(name=str,age=int,gender=str,salary=float)
    24 class People:
    25     def __init__(self,name,age,gender,salary):
    26         self.name = name #触发set
    27         self.age = age
    28         self.gender = gender
    29         self.salary = salary
    30 
    31 p1 = People("Jenny",20,"female",90)
    32 print(People.__dict__)
    33 p2 = People('Arron',"twenty","male",500)
    34 
    35 结合装饰器应用
    结合描述器应用

    getitem 、setitem  和delitem 

    item只适合于字典的操作,attr适合用.方式操作

     1 class Foo:
     2     def __getitem__(self,item):
     3         print("getitem",item)
     4     def __setitem__(self,key,value):
     5         self.__dict__[key]=value
     6         print("setitem")
     7     def __delitem__(self,key):
     8         print("delitem")
     9         self.__dict__.pop(key)
    10 
    11 f = Foo()
    12 f["name"] = "Jenny"
    13 f["age"] = 18
    14 f["gender"] = "female"
    15 print(f.__dict__)
    16 # setitem
    17 # setitem
    18 # {'name': 'Jenny', 'age': 18}
    19 del f.age
    20 print(f.__dict__)
    21 {'name': 'Jenny'}
    22 del f["name"]
    23 # delitem
    24 f.gender
    25 f["gender"]
    26 # getitem gender
    item系列使用字典操作

     

    str和repr 

    改变对象的字符串显示,使用print

     1 # 定义str
     2 class Foo:
     3     def __init__(self,name,age):
     4         self.name = name
     5         self.age = age
     6     def __str__(self):
     7         return "名字是%s,年纪是%s"%(self.name,self.age)
     8 
     9 f = Foo("Jenny",18)
    10 print(f)
    11 # 名字是Jenny,年纪是18
    12 
    13 # 不定义str
    14 class Foo:
    15     def __init__(self,name,age):
    16         self.name = name
    17         self.age = age
    18 f = Foo("Jenny", 18)
    19 print(f)
    20 # <__main__.Foo object at 0x00000242D9E4FB70>
    str

    repr用于解释器中

     二者共存的效果

     1 class Foo:
     2     def __init__(self,name,age):
     3         self.name = name
     4         self.age = age
     5     def __str__(self):
     6         return self.name
     7     def __repr__(self):
     8         return "名字是%s,年纪是%s"%(self.name,self.age)
     9 
    10 f = Foo("Jenny",18)  #str(f)= f.__str__ 找不到才=> f.__repr__
    11 print(f)
    12 # Jenny
    str优先repr

    自定义format

     1 # 不定义自身的format函数
     2 class Date:
     3     def __init__(self,year,mon,day):
     4         self.year = year
     5         self.mon = mon
     6         self.day = day
     7 d1= Date(2018,10,26)
     8 x = "{0.year}{0.mon}{0.day}".format(d1)
     9 print(x)
    10 # 20181026
    11 
    12 # 定义自身的format函数
    13 format_dic = {
    14     "ymd":"{0.year}{0.mon}{0.day}",
    15     "mdy":"{0.mon}:{0.day}:{0.year}",
    16     "dmy":"{0.day}-{0.mon}-{0.year}"
    17 }
    18 class Date:
    19     def __init__(self,year,mon,day):
    20         self.year = year
    21         self.mon = mon
    22         self.day = day
    23     def __format__(self,format_spec):
    24         print("running")
    25         if not format_spec or format_spec not in format_dic:
    26             format_spec = "ymd"
    27         fm = format_dic[format_spec]
    28         return fm.format(self)
    29 
    30 d1= Date(2018,10,26)
    31 print(format(d1))
    32 print(format(d1,"mdy"))
    33 # running
    34 # 20181026
    35 # running
    36 # 10:26:2018
    自定义format

    slots属性

    1.slots是什么? 是一个类变量,变量值可以是列表、元组或可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

    2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

    3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。

    当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,是能使用在__slots__中定义的那些属性名。

    4.注意:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。大部分情况,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__,比如:在程序中需要创建某个类的几百万个实例对象。

    关于__slots__的常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这种目的,但这不是它的使用初衷,更多是作为一个内存优化的工具。

    1 class Foo:
    2     __slots__ = "name"
    3 f1 = Foo()
    4 f1.name = "Dragon"
    5 print(f1.name)
    6 # Dragon
    7 # f1.age = 18 #已经没有fi.__dict__了
    8 # # Wrong
     1 class Foo:
     2     __slots__ = ["name","age"]
     3 f1 = Foo()
     4 f1.name = "Dragon"
     5 f1.age = 18
     6 print(f1.__slots__)
     7 print(f1.age)
     8 # ['name', 'age']
     9 # 18
    10 f2 = Foo()
    11 f2.age = 90
    12 print(f2.age)
    13 # 90
    列表形式

    doc属性

    获取文档信息,该属性无法被继承

    1 class Foo:
    2     "描述信息"
    3     pass
    4 print(Foo.__doc__)
    5 # 描述信息
    doc

    __module__和  __class__ 

    __module__查看来自哪个模块

    __class__查看来自哪个类

    1 from aa.bb import Foo
    2 f1 = Foo()
    3 print(f1.__module__)
    4 print(f1.__class__)
    5 # ABCD
    6 # aa.bb #模块信息
    7 # <class 'aa.bb.Foo'> #类信息
    module class

    __del__析构方法 

    当对象在内存中被释放时,自动触 发执行。

    注:此方法一般无需定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交由Python解释器执行的,所以析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

     1 class Foo:
     2     def __init__(self,name):
     3         self.name = name
     4     def __del__(self):
     5         print("del is running")
     6 f1=Foo("alex")
     7 del f1
     8 print("=====>")
     9 # del is running #删除实例所以触发__del__
    10 # =====> #执行完毕继续运行后面的程序
    del

    del只在实例被删除的时候被触发,而不是属性被删除就触发

     1 class Foo:
     2     def __init__(self,name):
     3         self.name = name
     4     def __del__(self):
     5         print("del is running")
     6 f1=Foo("alex")
     7 del f1.name
     8 print("=====>")
     9 # =====> #没有先触发__del__而是先执行后面的程序
    10 # del is running #整个程序执行完毕内存释放所以__del__运行
    删除属性的效果

    __call__ 

    对象后面加括号,触发执行。

    注:构造方法的执行是由创建对象触发的,即:对象 = 类名(); 对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()

    1 class Foo:
    2     def __call__(self):
    3         print("ABCD")
    4 f1 = Foo() #创建了实例
    5 f1() # Foo下的__call__
    6 # ABCD
    call

    __next__ 和__iter__实现迭代器协议

    从迭代器协议讲起

     1 class Foo:
     2     def __init__(self,n):
     3         self.n = n
     4     def __iter__(self):
     5         return self
     6     def __next__(self):
     7         self.n += 1
     8         return self.n
     9 f1= Foo(10)
    10 print(f1.__next__())
    11 print(f1.__next__())
    12 print(next(f1))
    13 print(next(f1))
    14 # 11
    15 # 12
    16 # 13
    17 # 14
    迭代器
     1 class Fib:
     2     def __init__(self):
     3         self._a = 1
     4         self._b = 1
     5 
     6     def __iter__(self):
     7         return self
     8 
     9     def __next__(self):
    10         if self._a > 100:
    11             raise StopIteration("终止")
    12         self._a, self._b = self._b, self._a + self._b
    13         return self._a
    14 f1 = Fib()
    15 print(next(f1))
    16 print(next(f1))
    17 print(next(f1))
    18 print(next(f1))
    19 print(next(f1))
    20 print(next(f1))
    21 print("====>")
    22 for i in f1:
    23     print(i)
    24 # 1
    25 # 2
    26 # 3
    27 # 5
    28 # 8
    29 # 13
    30 # ====>
    31 # 21
    32 # 34
    33 # 55
    34 # 89
    35 # 144
    斐波那切数列

    property补充

     1 class Foo:
     2     @property
     3     def AAA(self):
     4         print('get AAA')
     5     @AAA.setter
     6     def AAA(self,value):
     7         print("set AAA")
     8     @AAA.deleter
     9     def AAA(self):
    10         print("delete AAA")
    11 
    12 f1=Foo()
    13 f1.AAA='aaa'
    14 del f1.AAA
    15 # set AAA
    16 # delete AAA

    这种效果也一样

     1 class Foo:
     2     def get_AAA(self): #这几个函数的顺序不可改变
     3         print('get AAA')
     4     def set_AAA(self,value):
     5         print("set AAA")
     6     def del_AAA(self):
     7         print("delete AAA")
     8     AAA=property(get_AAA,set_AAA,del_AAA)
     9 f1=Foo()
    10 f1.AAA='aaa'
    11 del f1.AAA
    12 # set AAA
    13 # delete AAA

    metaclass 元类

    1、引子

    Python中一切皆对象,类本身也是一个对象。当使用关键字class时,python解释器在加载class的时候就会创建出一个对象(这里的对象指的是类而非类的示例)

    1 class Foo:
    2     pass
    3 f1 = Foo()
    4 print(type(Foo))
    5 print(type(f1))
    6 # <class 'type'> #类的类就是type
    7 # <class '__main__.Foo'> #表示obj对象由Foo类创建

    2、什么是元类

    元类是类的类,是类的模板

    元类是用来控制如何创建类的,正如类是创建对象的模板一样

    元类的示例为类,正如类的实例为对象

    type是python内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

    3、创建类的两种方式

     1 class Foo:
     2     def __init__(self,name):
     3         self.name = name
     4 print(Foo)
     5 def __init__(self,name):
     6     self.name = name
     7 def test(self):
     8     print("test------")
     9 FFo = type("FFo",(object,),{"x":1,"__init__":__init__,"test":test})
    10 print(FFo)
    11 # <class '__main__.Foo'>
    12 # <class '__main__.FFo'>
    13 f1 = FFo("alex")
    14 print(f1.x)
    15 # 1
    16 f1.test()
    17 # test------

    4、一个类没有声明自己的元类,则默认其元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类

     1 class MyType(type):
     2     def __init__(self,a,b,c):
     3         print("我的元类")
     4         print(a) #类名
     5         print(b) #继承的东西
     6         print(c) #属性字典
     7     def __call__(self, *args, **kwargs):
     8         print("====>")
     9         obj = object.__new__(self) #object.__new__(Foo)->f1
    10         self.__init__(obj,*args, **kwargs) #Foo.__init__(f1,*args, **kwargs)
    11         return obj
    12 class Foo(metaclass=MyType): #MyType("Foo",(object,),{})触发init
    13     def __init__(self,name):
    14         self.name = name
    15 print(Foo)
    16 f1=Foo("alex")
    17 print(f1.__dict__)
    18 # 我的元类
    19 # Foo
    20 # ()
    21 # {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x0000025E46C79598>}
    22 # <class '__main__.Foo'>
    23 # ====>
    24 # {'name': 'alex'}
    自定义元类MyType
  • 相关阅读:
    2020年秋第四五周-代码规范,结对要求
    2020年秋第四五周-四则运算试题生成
    同时装了WPS和Office新建的时候不知道是哪个文件
    开讲啦郑强演讲:你为什么读大学?
    PC版kindle无法连接网络
    前端编程良好习惯
    教你隐藏盘符,把你的小姐姐藏起来
    腾讯,比你想的更有趣
    U盘之父中国朗科的一生:我曾打败索尼,如今却只能靠收租为生
    动作之概述
  • 原文地址:https://www.cnblogs.com/jennifer224/p/12486584.html
Copyright © 2011-2022 走看看