Python的attr三兄弟(getattr、hasattr、setatter)

阅读源码的时候,经常看见hasattrgetattrsetattr,感觉很酷炫的同时,也是不解其义,特此来说说道说道这三兄弟

getattr已经介绍过了,主要说说setattr和hasattr

hasattr(对象,属性名)Python的attr三兄弟(getattr、hasattr、setatter)_第1张图片

I、基本操作
  • 1、查看对是不是该属性,返回值为boolean类型
  • 2、 一个危险的Python函数,不推荐使用这篇文章说道hasattr会把@property装饰的方法隐藏掉,以至于使用第三方库的时候,不知道对方是否使用了这个装饰器,而造成错误。
  • 3、但是这篇文章是三年前写的,这个问题已经被修复了。可以愉快的使用了,但是如果使用Python2x或者需要编写适配Python全系列的话,还是不要用太多
class Test:
   @property
   def name(self):
       return "Despair"

   @property
   def age(self):
       return 11


T = Test()
print(hasattr(T, 'name'))
print(getattr(T, 'name', None))
print(hasattr(T, 'age'))
print(getattr(T, 'age', None))
-----
True
Despair
True
11
II、速度效益
  • 1、上面那篇文章说,hasattr内部实现是调用getattr,经过试验,这个也被改过了,hasattr的速度并不比getattr慢
from timeit import timeit


description = '''\
class Test:
    @property
    def name(self):
        return 'Donald'
a = Test()
'''

stm = '''\
hasattr(a,'name')
'''
print(f'hasattr 执行10000次所需时间:{timeit(stmt=stm, setup=description, number=10000)}')

stm = '''\
getattr(a, 'name', None)
'''
print(f'getattr 执行10000次所需时间:{timeit(stmt=stm, setup=description, number=10000)}')

-----
hasattr 执行10000次所需时间:0.0015951709999999994
getattr 执行10000次所需时间:0.0016753230000000029
  • 2、 可以看出来,运行时间hasattr还是略胜一筹,在Python3x中,hasattr还是可以愉快的玩耍的

setattr(对象,key, value)

I、基础操作
  • 1、给对象添加一个属性,相当于在对象的__dict__中再添加一组键值对
  • 2、如果是自己写的对象或者第三方的对象,是只可以直接使用对象.__setattr__(key,value)等效于setattr()。但是对自带的几种基本数据类型不起作用···具体原因,我目前不清楚
class Test:
      def __init__(self):
            self.name = "Donald"

      def name(self):
           return self.name  

a = Test()
print(a.__dict__)
setattr(a, 'a', 'hello')               # setattr()
print(a.__dict__)
a.__setattr__('b', 'world')           #__setattr__()
print(a.__dict__)

*****
# 第三方模块
import requests
b = requests.session()     # request的一个方法
print(b.__dict__)
b.__setattr__('a', 'hello')
print(b.__dict__)
------
# 新加的在最后面
{'headers': {'User-Agent': 'python-requests/2.19.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}, 'auth': None, 'proxies': {}, 'hooks': {'response': []}, 'params': {}, 'stream': False, 'verify': True, 'cert': None, 'max_redirects': 30, 'trust_env': True, 'cookies': , 'adapters': OrderedDict([('https://', ), ('http://', )])}
{'headers': {'User-Agent': 'python-requests/2.19.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}, 'auth': None, 'proxies': {}, 'hooks': {'response': []}, 'params': {}, 'stream': False, 'verify': True, 'cert': None, 'max_redirects': 30, 'trust_env': True, 'cookies': , 'adapters': OrderedDict([('https://', ), ('http://', )]), 'a': 'hello'}


*****
# 数据类型
a = set()
print(a.__dict__)
------
AttributeError: 'set' object has no attribute '__dict__'

II、魔法方法
  • 1、既然都有__setattr__这个用法了,在定义类的时候,肯定也是可以制定的
  • 2、重写了__setattr__方法之后,添加一个新的属性的时候,会调用这个方法
  • 3、定制__setattr__方法的时候,他传入的值不能直接使用,否则会陷入无限递归中
class Test:
    """测试类"""

    @staticmethod
    def name():
        return "Donald"

    def __setattr__(self, key, value):
        print(f'key->{key}')
        self.key = value


a = Test()
a.age = 10
-------------
key->key
  ·   # 很多个
  ·
key->key
RecursionError: maximum recursion depth exceeded while calling a Python object
  • 4、正确的写法是在实例属性的列表中植入,如:self.__dict__[key] = value
class Test:
    """测试类"""

    @staticmethod
    def name():
        return "Donald"

    def __setattr__(self, key, value):
        print(f'key->{key}')
        self.__dict__[key] = value


a = Test()
a.age = 10
print(a.__dir__())
-----
key->age
['age', '__module__', '__doc__', 'name', '__setattr__', '__dict__', '__weakref__', '__repr__', '__hash__', '__str__', '__getattribute__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__init__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']

  • 5、但是感觉这个魔法方的用武之地少的可怜,基本上都不会重写这个魔法方法

综上所所述:getattr在日常开发中用到的最多,当属老大,hasattr紧随其后,setattr的地位感觉会被property冲击

你可能感兴趣的:(Python小站)