Python之路(八)——Python OOP(进阶篇)
本节内容:
- 一切皆对象
- 元类
- 类的内置属性
- 描述符
- 类的装饰器
- 再看@property
- 反射
一、一切皆对象
统一两点
- 对象与类为实现关系,类与类为继承关系
- 谈对象和类是在特定场合下才有效,即 语言的场景性
使用内置属性查看对象的类
type vs instance vs issubclass
class Fu: pass class Zi(Fu): pass class Sun(Zi): pass z = Zi() print(isinstance(z,Fu))#判断一个对象是否为一个类的实例,可传递 print(isinstance(z,Zi)) print(type(z) is Fu) #False.仅判断实例和其产生的直接类的关系 print(type(z) is Zi) print(issubclass(Zi,Fu)) #判断两个类的关系,可传递 print(issubclass(Sun,Fu))
python中类与对象关系图
说明:
- 实现关系:横向为实现关系,能够生成类的结构叫“元类”,生成的类本身也是对象所以也叫类对象
- 继承关系:纵向为继承关系,内置的类都继承自object
- 用户一般使用第二、三列,自定义类(MyClass 默认继承自object);常用变量(var2)是第二列内置类(str)的实现
二、元类
类的动态创建
- 类代码定义在函数中,动态调用函数
- 使用内部函数 type(name, bases, dict)动态创建
#方法一、函数内部定义类,动态调用函数创建 def choose_class(class_name): if class_name == 'foo': class Foo: pass return Foo if class_name == 'bar': class Bar: pass return Bar foo = choose_class('foo') print(foo) # <class '__main__.choose_class.<locals>.Foo'> #分析:类的代码还需要提前书写 #方法二、type(name, bases, dict) -> a new type # class MyClass: # bar = True #两者等价 MyClasss = type('MyClass',(object,),{'bar':True}) print(MyClasss,MyClasss.bar) # <class '__main__.MyClass'> True
元类
创建类这种对象的类,type为Python中的内建元类
自定义元类
class UpperAttrMetaclass(type): #建议继承内部元类创建自己元类 def __new__(cls, name, bases, dct): attrs = ((name, value) for name, value in dct.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr) class Foo(object,metaclass=UpperAttrMetaclass): # __metaclass__ = UpperAttrMetaclass 3.7测试无效的 bar = 'bip' print(Foo.__dict__) # {'BAR': 'bip'...} 类的属性名被转换为大写
三、内置属性
由于一切皆对象,python中的内置函数和操作符号(例如:.、 [] 、()、with 等)都对应类(对象)中的内置方法
内置方法很杂,按OOP思想要调用应该把内置方法封装为接口,由用户来实现(个人理解)
内置数据属性
- __doc__ :类文档,子类不继承
- __module__:类的模块名(含包名)
- __dict__:类属性,含数据和方法属性,对象不显示类中的属性
- __bases__:父类,多个用元祖组成
- __class__:当前类名
内置方法属性
- __new__ 创建对象,静态方法,类() 触发
- __init__ 初始化对象,由__new__返回本类对象时触发
# __new__ vs __init__ #1. # class A(object): # def __init__(self): # print("init") # #默认为类方法,cls 内容由解释器自动传入,args 为父类元祖,kwargs 为类属性字典 # def __new__(cls,*args, **kwargs): # print("new %s"%cls) # return object.__new__(cls, *args, **kwargs) #返回一个对象可以是自己的也可以是其他的类, # # A() # 结果: # new <class '__main__.A'> # init #2. class A(): pass class B(A): def __init__(self): print("init") #默认为类方法,cls 内容由解释器自动传入,args 为父类元祖,kwargs 为类属性字典 def __new__(cls,*args, **kwargs): print("new %s"%cls) return object.__new__(A, *args, **kwargs) #返回一个对象可以是自己的也可以是其他的类, B() # new <class '__main__.B'>
- __del__: del 对象时触发
class A(): def __del__(self): print('__del__被执行了') a = A() del a # __del__被执行了。对象的内存释放由解释器来完成,程序员不用考虑
- __str__ :显示对象打印格式
- __repr__:同上,区别:命令行下,执行repr(对象名)打印,一般用作程序调试
class Employee: def __init__(self,name,age,salary): self.name = name self.age = age self._salary = salary def __str__(self): return f'name: {self.name}, age:{self.age}' def __repr__(self): return self.__str__() #懒惰写法 e = Employee('alex', 20, 5000) # print(e) # 默认打印 <__main__.Employee object at 0x00000193C7C73EB8> print(e) # __str__ name: alex, age:20 。 __repr__不会被print 等用户使用的函数调用
- __getattr__ :对象取属性(不包含方法属性调用)触发
- __setattr__:对象属性被修改时(包含方法属性调用)触发
- __delattr__:删除对象属性时触发
- __getattribute__:"对象名." 形式就会触发,不区分赋值还是取值,删除等操作。
class Foo: x = 1 def __init__(self, y): self.y = y #会触发 setattr #主要对象. 这种形式都会触发 def __getattribute__(self, item): print('----> from getattribute') def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key = value #错误,引发死循环 # self.__dict__[key] = value #这种写法 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 # self.__dict__.pop(item) # __setattr__添加/修改属性会触发它的执行 f1 = Foo(10) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z = 3 print(f1.__dict__) # __delattr__删除属性的时候会触发 # f1.__dict__['a'] = 3 # 直接修改属性字典,不触发 del f1.a print(f1.__dict__) # # __getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx
- __getitem__:同__getattr__,不过不是采用"."来操作而是"[ ]"
- __setitem__
- __delitem__
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1=Foo('sb') f1['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alex' print(f1.__dict__)
- __call__:对象名() 时被触发
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__
class Meta(type): #2. 生成类对象 def __new__(cls, name, bases, dct): print("calling Meta's __new__", cls) return type.__new__(cls, name, bases, dct) #4.类对象调用触发元类的__call__ 方法 def __call__(cls, *args, **kwargs): print("calling Meta's __call__", cls) #5.类对象,从自己的对象觉都看也是一个类,可以直接调用自己的静态方法 __new__ 生成自己对象 i = cls.__new__(cls) #6.类对象生成的对象初始化 i.__init__(*args, **kwargs) return i class A(metaclass=Meta): # 1. 类对象的创建使用 元类的__new__ def __new__(cls, *args, **kwargs): print("calling A's __new__") return object.__new__(cls) def __init__(self, *args, **kwargs): print("calling A's __init__") #3.类对象调用 A() # calling Meta's __new__ <class '__main__.Meta'> # calling Meta's __call__ <class '__main__.A'> # calling A's __new__ # calling A's __init__
- __enter__ :进入with 代码块被执行
- __exit__ : 退出with 代码块前被执行
class Open: def __init__(self,filepath,mode='r',encoding='utf-8'): self.filepath=filepath self.mode=mode self.encoding=encoding def __enter__(self): # print('enter') self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) return self.f def __exit__(self, exc_type, exc_val, exc_tb): # print('exit') self.f.close() return True #不处理异常 def __getattr__(self, item): return getattr(self.f,item) with Open('a.txt','w') as f: print(f) f.write('aaaaaa') f.wasdf #抛出异常,交给__exit__处理
-
__next__ : 定义可迭代对象
- __iter__: 定义迭代器
class Fib: def __init__(self): self._a=0 self._b=1 def __iter__(self): return self def __next__(self): self._a,self._b=self._b,self._a + self._b return self._a f1=Fib() print(f1.__next__()) print(next(f1)) print(next(f1)) #继续迭代 for i in f1: if i > 100: break print('%s ' %i,end='')
四、描述符
描述符本质就是一个新式类(Python3都是新式类),在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个。其作用是用来代理另外一个类的类属性
#描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') #描述符Int class Int: def __get__(self, instance, owner): print('Int调用') def __set__(self, instance, value): print('Int设置...') def __delete__(self, instance): print('Int删除...') class People: name=Str() age=Int() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age p1=People('alex',18) #Str设置... # Int设置... print(p1.__dict__) # {} print(People.name) # Str调用,自动触发描述符__get__ 方法 print(People.__dict__) # 'name': <__main__.Str object at 0x000001CC5EAD2208>, 'age': <__main__.Int object at 0x000001CC5EAD2240>
分类
-
描述符中仅定义__get__,称 非数据描述符
-
定义了__set__或者__del__之一 ,称 数据描述符
优先级(从高到低)
- 类属性
- 数据描述符
- 实例属性
- 非数据描述符
- __getattr__(没有属时调用)
#数据描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') #非数据描述符 class Int: def __get__(self, instance, owner): print('Int调用') class People: name=Str() age = Int() def __init__(self,name,age): #name被Str类代理 self.name = name self.age = age def __getattr__(self, item): print("没有找到") #1.类属性调用 # People.name = 'alex' # People.age = 18 #相当于覆写了字典 # print(People.name, People.age) # print(People.__dict__) #2.对象属性 p1 = People('alex2',118) print(p1.name, p1.age) print(p1.__dict__) # Str设置... # Str调用 # None 118 # {'age': 118} #非数据描述符代理 没有启用; 数据描述符代理 启用 #3.没有定义的属性 # p1.PPP #触发__getattr__方法
作用<例:实现对象初始化参数类型限制>
#参数类型限制 class Typed: def __init__(self,name,expected_type): self.name=name self.expected_type=expected_type def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) if not isinstance(value,self.expected_type): raise TypeError('Expected %s' %str(self.expected_type)) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Typed('name',str) age=Typed('name',int) salary=Typed('name',float) def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('123',18,3333.3) # set---> <__main__.People object at 0x0000019D389623C8> 123 # set---> <__main__.People object at 0x0000019D389623C8> 18 # set---> <__main__.People object at 0x0000019D389623C8> 3333.3 # p1=People('egon','18',3333.3) # p1=People('egon',18,3333)
五、类的装饰器
装饰器形式:
- 被装饰对象:类、普通函数、类方法
- 装饰者:普通函数;描述符方法(用描述符自身代替 @描述符名)
回顾装饰器(@xxx)
#1.简单装饰器 # def wrapper(fun,*args,**kwargs): # print("简单装饰器") # return fun # # @wrapper #@装饰器名相当于: test1=wraperr(test1(*args,**kwargs)) # def test1(a,b): # print("test1",a,b) # test1(2,3) #2.带参数的装饰器 def log(log_level): def wrapper(fun,*args,**kwargs): if log_level == 'Error': print("Error 级别的日志") return fun return wrapper @log('info') #带参数的装饰器相当于:1.执行带参函数返回装饰器 2. test1=wraperr(test1(*args,**kwargs)) def test1(a,b): print("test1",a,b)
类的装饰器<重构类型限制>
class Typed: def __init__(self,name,expected_type): self.name=name self.expected_type=expected_type def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) if not isinstance(value,self.expected_type): raise TypeError('Expected %s' %str(self.expected_type)) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) def typeassert(**kwargs): def decorate(cls): print('类的装饰器开始运行啦------>',kwargs) for name,expected_type in kwargs.items(): setattr(cls,name,Typed(name,expected_type)) return cls return decorate @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary print(People.__dict__) p1=People('egon',18,3333.3)
类方法加装饰器<制作property>
#模拟@property class Lazyproperty: def __init__(self,func): self.func=func def __get__(self, instance, owner): print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()') if instance is None: return self return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情 class Room: def __init__(self,name,width,length): self.name=name self.width=width self.length=length @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符 def area(self): return self.width * self.length r1=Room('alex',1,1) print(r1.area)
描述符与装饰器
- 描述符:对类的属性(数据,方法)进行描述的类。在被代理类的属性访问时触发__get__,__set__,__del__
- 装饰器本质也是实现一种代理功能。采用@语法糖,触发条件取决于代理者
六、再看@property
Python内置的描述符property,采用装饰器@的方式可以对类数据属性的访问进行控制
用法一
# 用法一、 class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA
用法二
#用法二 class Foo: def get_AAA(self): print('get的时候运行我啊') def set_AAA(self,value): print('set的时候运行我啊') def delete_AAA(self): print('delete的时候运行我啊') AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应
七、反射
反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。——from 百度
- __hasattr__
- __getattr__
- __setattr__
- __delattr__
#动态生成函数 class GreetMe: def __init__(self, name): self.name = name def __getattr__(self, attr): allowed = ['hello', 'goodbye', 'goodnight'] def call(name=None): if attr in allowed: target = name if name else self.name print(f"{target} {attr.capitalize()}") else: raise AttributeError(f"{attr} is not in {__class__}") return call greet = GreetMe('Luna') greet.hello('Peter') greet.goodbye() greet.he2llo('Peter')