声明:这里所说的类实例就是初始化一个类,然后建一个对象如:
a = A();
类对象,是类本身,因为类也是一个对象。
hasattar()
判断某个属性是否存在于某个类的属性集合中(包括函数,函数也是属性),如果不是,则返回false。
以下代码为例,我们初始化A的对象时,给所有的A的实例分配了一个属性value,每个实例都有此属性,只是不共享(注意,这不是类对象的属性,类对象的属性对类的实例来说,是全局变量)。那么,我们可以利用hasattar()判断一个属性是不是在一个对象属性的集合中。
class A(object):
def __init__(self, value):
self.value = value
def __getattr__(self, item):
print("找不到此属性")
return super().__getattribute__(item)
a = A(10)
print(hasattr(a,"value"))
#结果为True
通过代码判断,value确实是属于A的某一个实例的属性,结果为True。假如,我们把value改成value1呢?通过代码判断,value1并不是A实例的属性。从代码中可以看出,调用hasattr()时,会自动调用__getattr__()去访问A实例的属性,如果类实例没有这个属性,就会返回False。
class A(object):
def __init__(self, value):
self.value = value
def __getattr__(self, item):
print("找不到此属性")
return super().__getattribute__(item)
a = A(10)
print(hasattr(a,"value1"))
结果为:
找不到此属性
False
事实上我们不仅可以在hasattr()函数中的第一个参数放入类实例,也可以放类对象,因为类也是一个对象,或者说,类也是它上层类的一个实例(注意,这里说的上层类不是父类)。那么自然而然就可以放类对象。但是我们不能把hasattr()函数的第二个参数放类实例的属性,因为实例的属性只属于类实例,而不属于类对象,对类型对应的属性对类实例来说,是全局变量。
class A(object):
name = "小明"
def __init__(self, value):
self.value = value
def __getattr__(self, item):
print("找不到此属性")
return super().__getattribute__(item)
a = A(10)
print(hasattr(a,"value"))
print(hasattr(A,"value"))
print(hasattr(A,"name"))
结果为:
True
False
True
从结果可以得知,第二个结果为False,说明前面说的“实例的属性只属于类实例,而不属于类对象,对类型对应的属性对类实例来说,是全局变量”这句话是对的。有人可能奇怪,为什么“print(“找不到此属性”)”对应的这个函数为什么不执行,找不到属性的话,不是会执行__getattr__()函数吗?当然会执行,只不过执行的是A对象上层类的__getattr__()函数。
getattr()
获取某个对象的属性值,如果获取不到,则执行__getattr__函数抛出异常。
class A(object):
name = "小明"
def __init__(self, value):
self.value = value
self.key = "key"
def __getattr__(self, item):
print("找不到此属性")
return super().__getattribute__(item)
def test(self):
print("哈哈")
return "正在测试中"
a = A(10)
print(getattr(a,"key"))
#结果为:10
稍微该一行代码:
print(getattr(a,"key1"))
key1属性肯定不存在于类实例中的,结果会报错:
找不到此属性
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/pm2/pm2/app1/tests.py", line 17, in <module>
print(getattr(a,"key1"))
File "C:/Users/Administrator/Desktop/pm2/pm2/app1/tests.py", line 9, in __getattr__
return super().__getattribute__(item)
AttributeError: 'A' object has no attribute 'key1'
我们还可以对print(getattr(a,“key1”))这一行代码进行改造
那就是加一个默认值,print(getattr(a,“key1”,“我是默认值”)),
如果实例中没有“key1"这个属性,getattr()就会返回这个默认值,防止程序报错。
那我们把第二个参数传递为函数属性呢?同样可以获取函数属性的值,即函数地址。如果向执行的话,加个()括号调用下就行了。
print(getattr(a,"test1","我是默认值"))
结果为:
<bound method A.test of <__main__.A object at 0x00000280976408C8>
加上括号后:
print(getattr(a,"test1","我是默认值")())
结果为:
哈哈
正在测试
setattr函数
稍微修改以下源代码:
class A(object):
name = "小明"
def __init__(self, value):
self.value = value
self.key = "key"
def __setattr__(self, key, value):
self.__dict__[key] = value
def test(self):
print("哈哈")
return "正在测试中"
a = A(10)
setattr(a,"key","我是修改后的key")
print(a.key)
#执行结果:我是修改后的key
当试图对象的item特性赋值的时,_setattr()将会被调用。
__setattr__依赖setattr()而存在。(其实也可以理解为,一个类实例就是一个字典,属性为”key“,值为"value",其实就是修改这个字典的key所对应的值)
给对象的属性赋值,若属性不存在,则先创建再赋值
```python
class A(object):
name = "小明"
def __init__(self, value):
self.value = value
self.key = "key"
def __setattr__(self, key, value):
self.__dict__[key] = value
print("__setattr__被调用")
def test(self):
print("哈哈")
return "正在测试中"
a = A(10)
setattr(a,"new_key","我是新建立的key")
print(a.new_key)
结果为:
__setattr__被调用
__setattr__被调用
__setattr__被调用
我是新建立的key
前两个”__setattr__被调用“是因为__init__函数里有两次赋值行为,会默认子弟哦那个调用__setattr__方法,最后一个才是因为”setattr(a,“new_key”,“我是新建立的key”)“这句代码。
通过setattr()\进行属性赋值和通过对象.属性=xxx进行赋值或者修改并无区别,都会调用__setattr__方法,可以结合字典的用法,无非就是增删查改,事实上,和字典用法并无实质区别(也可以结合《流畅的python》这本书,看一下,如何将字典模拟成类进行,然后进行类似于类的操作):
class A(object):
# name = "小明"
def __init__(self, value):
self.value = value
self.key = "key"
def __setattr__(self, key, value):
self.__dict__[key] = value
print("__setattr__被调用")
def test(self):
print("哈哈")
return "正在测试中"
a = A(10)
setattr(a,"new_key","我是新建立的key")
print(a.new_key)
a.new_key=2
结果为:
__setattr__被调用
我是新建立的key
__setattr__被调用
__setattr__和__getattr__结合之前的setattr和getattr的用法看一下