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')
浙公网安备 33010602011771号