社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
from datetime import date
class User:
def __init__(self, name, birthday):
self.name = name
self.birthday = birthday
if __name__ == "__main__":
user = User("corley", date(year=2020, month=1, day=1))
print(user.name)
打印
corley
当打印不存在的属性时,会报错:
from datetime import date
class User:
def __init__(self, name, birthday):
self.name = name
self.birthday = birthday
if __name__ == "__main__":
user = User("corley", date(year=2020, month=1, day=1))
print(user.age)
打印
Traceback (most recent call last):
File "xxx/demo.py", line 18, in <module>
print(user.age)
AttributeError: 'User' object has no attribute 'age'
此时需要加入__getattr__
:
from datetime import date
class User:
def __init__(self, name, birthday):
self.name = name
self.birthday = birthday
def __getattr__(self, item):
print("not find attr")
if __name__ == "__main__":
user = User("corley", date(year=2020, month=1, day=1))
print(user.age)
打印
not find attr
None
可得:
None表示打印user.age
时调用__getattr__
方法,该方法没有返回值,所以打印None;__getattr__
魔法方法是在查找不到属性的时候调用。
同时可打印__getattr__
的参数item:
from datetime import date
class User:
def __init__(self, name, birthday):
self.name = name
self.birthday = birthday
def __getattr__(self, item):
print(item, "not find attr")
if __name__ == "__main__":
user = User("corley", date(year=2020, month=1, day=1))
print(user.age)
打印
age not find attr
None
即表示没有找到User对象的age属性。
在初始化方法的参数中增加info字典,初始化实例时info里添加age键值对:
from datetime import date
class User:
def __init__(self, name, birthday, info = {}):
self.name = name
self.birthday = birthday
def __getattr__(self, item):
print(item, "not find attr")
if __name__ == "__main__":
user = User("corley", date(year=2020, month=1, day=1),info={'age':18})
print(user.age)
打印
age not find attr
None
仍然查找不到age属性,要想能查找到age属性,需要改进代码:
from datetime import date
class User:
def __init__(self, name, birthday, info = {}):
self.name = name
self.birthday = birthday
self.info = info
def __getattr__(self, item):
return self.info[item]
if __name__ == "__main__":
user = User("corley", date(year=2020, month=1, day=1),info={'age':18})
print(user.age)
打印
18
此时即可访问到字典中的属性age。
如果要访问的属性在字典中不存在时,会报和字典的箭不存在一样的错误:
from datetime import date
class User:
def __init__(self, name, birthday, info = {}):
self.name = name
self.birthday = birthday
self.info = info
def __getattr__(self, item):
return self.info[item]
if __name__ == "__main__":
user = User("corley", date(year=2020, month=1, day=1),info={'age':18})
print(user.ages)
打印
Traceback (most recent call last):
File "xxx/demo.py", line 16, in <module>
print(user.ages)
File "xxx/demo.py", line 11, in __getattr__
return self.info[item]
KeyError: 'ages'
解决办法:
__getattr__
方法里访问字典的方式from datetime import date
class User:
def __init__(self, name, birthday, info = {}):
self.name = name
self.birthday = birthday
self.info = info
def __getattr__(self, item):
return self.info.get(item)
if __name__ == "__main__":
user = User("corley", date(year=2020, month=1, day=1),info={'age':18})
print(user.ages)
打印
None
如果找不到相应的键,会返回None。
__getattribute__
方法from datetime import date
class User:
def __init__(self, name, birthday, info = {}):
self.name = name
self.birthday = birthday
self.info = info
def __getattr__(self, item):
return self.info.get(item)
def __getattribute__(self, item):
return "corley"
if __name__ == "__main__":
user = User("corley", date(year=2020, month=1, day=1),info={'age':18})
print(user.age)
print(user.ages)
print(user.birthday)
打印
corley
corley
corley
易知:
不管访问User对象的什么属性,不管属性是否存在,都返回的是__getattribute__
方法的返回内容;
进一步可知,__getattribute__
方法在__getattr__
方法之前执行,不要轻易重写。
class User:
def __init__(self, age):
self.age = age
def get_age(self):
return str(self.age) + 'years old'
def set_age(self, age):
if not isinstance(age, int):
raise TypeError('Type Error')
self.age = age
该类可以对年龄属性进行判断,不过不是整型,则抛出异常;
但是显然这只是对一个属性进行判断,如果User类中有多个属性都需要判断,那么就需要写多个方法,就很麻烦,我们需要对方法进行复用。这个时候就要用到属性描述符。
属性描述符:
在类中实现 __get__
、__set__
、__del__
中的一个方法,即可构成属性描述符。
class IntField(object):
def __get__(self, instance, owner):
print('__get__')
def __set__(self, instance, value):
print('__set__')
def __del__(self, instance):
pass
class User:
age = IntField()
user = User()
#set
user.age = 30
#get
print(user.age)
打印
__set__
__get__
None
Exception ignored in: <function IntField.__del__ at 0x0000023DDEDC6C18>
TypeError: __del__() missing 1 required positional argument: 'instance'
并没有打印出30,但是__get__
和__set__
方法被调用,并且先调用__set__
方法。
class IntField(object):
def __get__(self, instance, owner):
print('__get__')
def __set__(self, instance, value):
print('__set__')
print(instance)
print(value)
def __del__(self, instance):
pass
class User:
age = IntField()
user = User()
#set
user.age = 30
#get
print(user.age)
打印
__set__
Exception ignored in: <function IntField.__del__ at 0x000001FCFE446C18>
<__main__.User object at 0x000001FCFE488388>
30
TypeError: __del__() missing 1 required positional argument: 'instance'
__get__
None
易知,__set__
方法的参数value即User对象属性赋值的值,在执行user.age = 30
时调用__set__
方法方法并传参数值。
进一步改进–加入属性值判断:
class IntField(object):
def __get__(self, instance, owner):
print('__get__')
return self.value
def __set__(self, instance, value):
print('__set__')
if not isinstance(value,int):
raise TypeError('Type Error')
self.value = value
def __del__(self, instance):
pass
class User:
age = IntField()
user = User()
#set
user.age = 30
#get
print(user.age)
打印
__set__
__get__
30
Exception ignored in: <function IntField.__del__ at 0x0000018F39F76C18>
TypeError: __del__() missing 1 required positional argument: 'instance'
此时能打印出正常的属性值,这就是属性描述符。
属性描述符分为数据描述符(有__get__
方法和__set__
方法)和非数据描述符(只有__get__
方法,较少),这里是数据描述符。
非数据描述符的定义如下:
class NoneDataIntField:
def __get__(self, instance, owner):
pass
如果传入的不是整型值,就会报错:
class IntField(object):
def __get__(self, instance, owner):
print('__get__')
return self.value
def __set__(self, instance, value):
print('__set__')
if not isinstance(value,int):
raise TypeError('Type Error')
self.value = value
def __del__(self, instance):
pass
class User:
age = IntField()
user = User()
#set
user.age = '30'
#get
print(user.age)
打印
__set__
Traceback (most recent call last):
File "xxx/demo.py", line 56, in <module>
user.age = '30'
File "xxx/demo.py", line 43, in __set__
raise TypeError('Type Error')
TypeError: Type Error
Exception ignored in: <function IntField.__del__ at 0x0000027003A66C18>
TypeError: __del__() missing 1 required positional argument: 'instance'
据此可对整型进行判断。
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
优先级最高的是属性描述符,下面进行简单的证明:
class IntField(object):
def __get__(self, instance, owner):
print('__get__')
return self.value
def __set__(self, instance, value):
print('__set__')
if not isinstance(value,int):
raise TypeError('Type Error')
self.value = value
def __del__(self, instance):
pass
class User:
age = IntField()
user = User()
#set
user.age = 30
user.__dict__['age'] = 18
#get
print(user.age)
打印
Exception ignored in: <function IntField.__del__ at 0x0000023854326C18>
TypeError: __del__() missing 1 required positional argument: 'instance'
__set__
__get__
30
易知,即便user.__dict__['age']
在user.age
后重新赋值18,返回的还是30,所以优先级最高的是属性描述符。
元类:
是创建类的类,即type类。
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))
打印
user
<class '__main__.create_class.<locals>.User'>
可以动态地创建User或者Student类。
查看type()
函数的源码:
def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
"""
type(object_or_name, bases, dict)
type(object) -> the object's type
type(name, bases, dict) -> a new type
# (copied from class doc)
"""
pass
可知type()函数有两种用法:
(1)type(object) -> the object's type
:
参数传入一个对象,返回这个对象的类型;
(2)type(name, bases, dict) -> a new type
:
给定一些参数,返回一个新类型,即创建类:
由第二种用法可知,type还可以动态的创建类type(类名,由父类组成的元组,包含属性的字典)
。
例如
User = type('User',(),{})
obj = User()
print(obj)
打印
<__main__.User object at 0x000001F17D88AB88>
向类中添加属性:
User = type('User',(),{'name':'corley'})
obj = User()
print(obj)
print(obj.name)
打印
<__main__.User object at 0x00000221F1D06688>
corley
向类中添加方法:
像添加属性一样添加方法
def info(self):
return self.name
User = type('User',(),{'name':'corley','info':info})
obj = User()
print(obj.info())
打印
corley
当方法名改变时,对象实例调用的方法名不需要改变,因为调用方法时调用的是字典里对应的键:
def infos(self):
return self.name
User = type('User',(),{'name':'corley','info':infos})
obj = User()
print(obj.info())
依然能正常运行,打印
corley
再如
def infos(self):
return self.name
def get_age(self):
self.age = 18
return self.age
def __init__(self):
self.sex = 'male'
User = type('User',(),{'name':'corley','info':infos,'age':get_age,'sex':__init__})
obj = User()
print(obj.info())
print(obj.age())
print(obj.sex)
print(obj.sex())
打印
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!