一.两种魔法方法
1.__getattr__方法
在类中调用属性时查找不到所要调用的属性时,执行该方法。
from datetime import date
class User:
def __init__(self, name, birthday):
self.name = name
self.birthday = birthday
def __getattr__(self, item): #未找到属性时执行
print('age not found')
print(item) #item即为未找到的属性名称
if __name__ == '__main__':
user = User('kris', date(year=1999, month=2, day=6)) #格式化日期
user.age #没有找到age属性
打印结果:
2.__getattribute__方法(属性拦截器)
每当调用属性时,都会先调用该方法(该方法作为了解):
from datetime import date
class User:
def __init__(self, name, birthday):
self.name = name
self.birthday = birthday
def __getattr__(self, item): #未找到属性时执行
print('age not found')
print(item) #item即为未找到的属性名称
def __getattribute__(self, item):
print('我最先执行!')
# return object.__getattribute__(self,item) #此处谨慎返回
if __name__ == '__main__':
user = User('kris', date(year=1999, month=2, day=6)) #格式化日期
user.age #没有找到age属性
user.name
打印如下:
二.属性描述符
属性描述符是指Python中实现了__get__()、__set __()、__delete __()方法中的任意一个方法的类。
属性描述符可以便捷地验证同一类的属性
class IntField:
#任意实现其一即为属性描述符
#instance为调用该描述符的实例对象,owner为调用该描述符的类对象
def __get__(self, instance, owner):
pass
#instance为调用该描述符的实例对象,value为所设置的属性值
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
1.数据描述符
实现了__get__()和__set__()方法的属性描述符:
#定义一个判断设置属性是否为整数的描述符
class IntField:
#访问属性
def __get__(self, instance, owner):
print('__get__')
return self.values
#设置属性
def __set__(self, instance, value):
print('__set__')
if not isinstance(value,int): #如果不是整数,报错
raise ValueError('ValueError')
self.values = value
class User:
age = IntField() #将属性描述符作为User类的一个属性
user = User()
user.age = 18 #调用属性描述符,设置属性
print(user.age) #调用属性描述符,访问属性
打印结果:
2.非数据描述符
只实现了__get__()方法的属性描述符:
class NoneDaTaInterField:
def __get__(self, instance, owner):
pass
3.属性查找顺序
user = User(), 那么user.age 顺序如下:
1 如果"age"是出现在User或其基类的__dict__中, 且age是data descriptor,那么调用其__get__方法, 否则
2 如果"age"出现在user的__dict__中, 那么直接返回 obj.dict[‘age’],否则
3 如果"age"出现在User或其基类的__dict__中
3.1 如果age是non-data descriptor,那么调用其__get__方法, 否则
3.2 返回 dict[‘age’]
4 如果User有__getattr__方法,调用__getattr__方法,否则
5 抛出AttributeError
重点记住最先查找属性描述符中的__get__()方法!!!
三.自定义元类
1.动态创建类
#动态创建类
def create_class(name):
if name == 'user':
class User:
def __str__(self):
return 'user'
return User #将创建好的类的引用返回
elif name == 'student':
class Student:
def __str__(self):
return 'student'
return Student
user = create_class('user') #调用函数,动态创建一个user类
print(user)
print(user()) #创建实例对象,调用__str__方法
打印如下:
2.使用元类创建类
由于Python语言的所有东西都是对象,因此类也是一个对象,同样可以将其像实例方法一样创建出来。
元类:创建类的类。type()函数除了可以查看对象所属类型之外,它还是一个类,一个创建类的类:
print(type(type)) #<class 'type'>
type的源码注释中这样写道:type(name, bases, dict) -> a new type 。
因此,我们可以用type来动态创建类,具体如下:
#其中类名为字符串,继承的父类为元组形式,属性方法以字典形式传入
A = type('A', (), {})
print(A) #<class '__main__.A'>
实例运用:
class Info:
def __init__(self, name, age):
self.name = name
self.age = age
def act(self):
print('playing basketball')
#用type动态创建一个类
Student = type('Student', (Info,), {'grade': 5, 'act': act})
kris = Student('kris',12)
print(kris.age) #调用继承的父类中的age属性,打印: 12
print(kris.grade) #调用类中的grade属性,打印: 5
kris.act() #调用类中的act方法,打印: playing basketball
3.metaclass属性
metaclass属性作为创建类似的参数,对其赋值后,Python会以元类的方式创建一个类并可控制该类的行为。
现在我们来使用metaclass属性创建一个类,使该类的所有属性大写:
#定义一个可改变创建的类的行为的方法,返回元类
def upper_attr(class_name, class_parents, class_attr):
new_attr = {}
for name,value in class_attr.items():
if not name.startswith('_'):
new_attr[name.upper()] = value
else:
new_attr[name] = value
print(new_attr)
return type(class_name, class_parents, new_attr) #返回元类
#使用metaclass属性以元类的方式创建类,并附加了一个大写属性的功能
class A(object,metaclass=upper_attr):
name = 'kris'
kris = A()
print(kris)
print(kris.NAME)
打印结果: