__getattr__与__getattribute__魔法函数
__getattr__方法可用来检查一个类中是否有一个属性,比如:
class User():
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print("not find attr")
def main():
user = User("dog")
user.age
if __name__ == '__main__':
main()
上面代码中User类中是没有age属性的,而我们调用user.age时也不会报错,而是运行了__getattr__里的内容,所以为了反正使用者用了一个类中没有的属性,我们就可以使用__getattr__。
__getattribute__方法也是类似的,不过他运行在__getattr__之前:
class User():
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print("not find attr")
def __getattribute__(self, item):
print("not find attribute")
def main():
user = User("dog")
user.age
if __name__ == '__main__':
main()
如上述代码运行后,打印的就不是not find attr,而是not find attribute了。
属性描述符
如果User类中有多个属性都需要判断,那么就需要写多个方法,这些方法怎么复用呢?这个时候就要用到属性描述符
class IntField(object):
"""
数据描述符
"""
def __get__(self, instance, owner):
print("get")
return self.values
def __set__(self, instance, value):
print("set")
if not isinstance(value, int):
raise ValueError('Value Error')
self.values = value
def __delete__(self, instance):
pass
class User:
age = IntField()
# age = 19
user = User()
user.age = 30
print(user.age)
属性描述符,只要实现了__get__,__set__,__delete__任何一个方法,就被称为属性描述符,在上述代码运行后,会依次打印出set get 30,,而如果讲age = 19 的注释去除,就不会打印出set get ,只会打印出30.
属性查找顺序
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
动态创建类
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
if __name__ == "__main__":
myclass = create_class('user')
obj = myclass()
print(obj)
print(type(obj))
在上述代码中,定义了一个create_class方法,该方法实现根据参数来创建一个类的功能,myclass = create_class(‘user’)相当于是创建了一个名字叫myclass的类,而这个类与create_class方法中定义的User类是一样的。obj = myclass()相当于实例化一个叫obj的myclass类。最后的打印结果为
user
<class '__main__.create_class.<locals>.User'>
使用type创建类
type除了查看一个对象的类型,还能用来创建一个类,像这种由类创建类的就叫元类:
使用方法为。type(类名,由父类组成的元组,包含属性的字典)
第一个参数:name表示类名称,字符串类型
第二个参数:bases表示继承对象(父类),元组类型,单元素使用逗号
第三个参数:attr表示属性,这里可以填写类属性、类方式、静态方法,采用字典格式,key为属性名,value为属性值
metaclass属性
如果一个类中定义了metaclass = xxx,Python就会用元类的方式来创建类
def upper_attr(class_name, class_parents, class_attr):
newattr = {}
for name, value in class_attr.items():
if not name.startswith("_"):
newattr[name.upper()] = value
return type(class_name, class_parents, newattr)
class Foo(object, metaclass=upper_attr):
# __metaclass__ = upper_attr
name = 'dog'
f = Foo()
print(hasattr(Foo, 'name'))
print(hasattr(Foo, 'NAME'))
其中 class_name 对应的是Foo,class_parents对应的是object,class_对应的是个字典,字典的键为Foo里的属性,值为属性对应的值。if not name.startswith("_"):用来过滤掉class_attr中的类似与__module__这种带_的。
最后打印出来的结果为 False True。
讲Foo里的name属性全大写为了NAME。
需注意的是,在python2里metaclass的用法为代码中的注释部分__metaclass__ = upper_attr,python3中的用法为代码中的用法class Foo(object, metaclass=upper_attr):。