python魔法函数__dict__和__getattr__的妙用

本文探讨了Python中的两个魔法函数:__dict__和__getattr__。__dict__用于存储对象属性,可以像字典一样操作。而__getattr__在尝试获取不存在的属性时会被调用,允许动态属性操作。通过示例展示了如何利用这两个函数简化代码,实现链式调用和优雅的数据访问方式。

参考  python魔法函数__dict__和__getattr__的妙用 - 云+社区 - 腾讯云

python魔法函数__dict__和__getattr__的妙用。

__dict__

__dict__是用来存储对象属性的一个字典,其键为属性名,值为属性的值。

既然__dict__是个字典那么我们就可以用字典的属性了。

我们通过使用dir()属性来看看__dict__都有哪些属性。

['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

我们看一段代码内含注释:

class A():
    def __init__(self):
        self.name="liming"

    def save_data(self,dicts):
        self.__dict__.update(dicts)#添加字典元素
        if isinstance(self.__dict__, dict):
            print(True)
       
        # 获取字典独有的属性
        print(set(dir(self.__dict__))-set(dir(self)))
        return self.__dict__

if __name__ == '__main__':
     dicts={"a":1,"b":2,"c":3}
     a=A()
     print(a.save_data(dicts))

输出结果:

True
{'__delitem__', 'keys', 'update', '__len__', '__getitem__', 'get', 'clear', 'copy', 'popitem', '__iter__', 'items', '__contains__', 'pop', '__setitem__', 'fromkeys', 'values', 'setdefault'}
{'name': 'liming', 'a': 1, 'b': 2, 'c': 3}

下面来一个比较实用的例子来大大的减少你的代码,做到真正的pythonic。
我们在使用给对象的属性赋值的时候:

class A():
    def __init__(self,dicts):
        self.name=dicts["name"]
        self.age=dicts["age"]
        self.sex=dicts["sex"]
        self.hobby=dicts["hobby"]

if __name__ == '__main__':
     dicts={"name":"lisa","age":23,"sex":"women","hobby":"hardstyle"}
     a=A(dicts)

我们看到我们需要换取传入的字典的各个键值,并创建键值同名一个属性,这里我们只有4个还好,想象一下如果我们传入的字典有100个键。上面代码简化为:

class A():
    def __init__(self,dicts):
        self.__dict__.update(dicts)
        print(self.__dict__)

if __name__ == '__main__':
     dicts={"name":"lisa","age":23,"sex":"women","hobby":"hardstyle"}
     a=A(dicts)

看完后感觉怎么样啊,其实__dict__还有一个重要的用处就是单例模式中共享同一状态,参考之前写的单例模式。拓展:部分内建函数不包含__dict__属性比如list,如果要查看list的属性怎么办呢,这时候用dir(list),dir方法也是查看对象的属性,包括内建对象的属性,但是它的输出形式列表,而__dict__是列表。

__getattr__

使用.获取属性的时候,如果该属性存在就输出其值,如果不存在则会去找_getatrr_,我们可以通过重写该方法可以实现动态属性的操作。(如果只允许添加指定的属性需要用__solts__函数控制)。先来一段比较有意思的代码:

from requests_html import HTMLSession
class UrlGenerator(object):
    def __init__(self, root_url):
        self.url = root_url
        self.session=HTMLSession()

    def __getattr__(self, item):
        if item == 'get':
            self.get_html()
        return UrlGenerator('{}.{}'.format(self.url, item))
    def get_html(self):
        req = self.session.get(self.url)
        print(req.text)

url_gen = UrlGenerator('https://www')
url_gen.baidu.com.get

充分利用__getattr__会在没有查找到相应实例属性时被调用的特点,方便的通过链式调用生成对应的url,在碰到get方法的时候调用函数获取其网页源码。
可调用的对象更加的优雅,链式的操作不仅优雅而且还能很好的说明调用的接口的意义。

下面展示一个__getattr__经典应用的例子,可以通过获取属性值的方式获取字典的键值。

class ObjectDict(dict):
    def __init__(self, *args, **kwargs):
        super(ObjectDict, self).__init__(*args, **kwargs)

    def __getattr__(self, name):
        value = self[name]
        if isinstance(value, dict):
            value = ObjectDict(value)
        return value

if __name__ == '__main__':
    od = ObjectDict(asf={'a': 1}, d=True)
    print(od.asf,od.asf.a)     # {'a': 1} 1
    print(od.d)                 # True

### Python 中 `__getattr__` 方法的用法 在 Python 的类定义中,`__getattr__` 是一种特殊的方法,用于处理访问不存在的属性的情况。当尝试访问对象的一个未定义属性时,如果该类实现了 `__getattr__` 方法,则会调用此方法来动态返回值或执行特定逻辑[^1]。 以下是关于 `__getattr__` 方法的一些重要特性使用场景: #### 特性描述 - 当通过点号操作符(`.`)访问一个实例的属性而找不到对应名称时,Python 会在抛出 `AttributeError` 前自动调用 `__getattr__` 方法。 - 需要注意的是,只有在常规属性查找失败的情况下才会触发 `__getattr__` 方法。因此,在类中显式定义的属性不会进入这个机制[^2]。 #### 使用示例 下面是一个简单的例子展示如何自定义行为以应对未知属性请求: ```python class DynamicAttributes: def __init__(self, initial=None): if initial is None: self.data = {} else: self.data = dict(initial) def __getattr__(self, name): """Called when an attribute lookup has not found the attribute in the usual places.""" try: return self.data[name] except KeyError as e: raise AttributeError(f"No such attribute: {e}") example = DynamicAttributes({"key": "value"}) print(example.key) # 输出 'value' # 下面这行将会引发 AttributeError 因为字典里没有对应的键 # print(example.nonexistent_key) ``` 在这个例子中,我们创建了一个名为 `DynamicAttributes` 的类,它允许存储任意数量的关键字参数作为内部数据结构的一部分,并且可以通过普通的点记法访问这些关键字所代表的数据项。如果没有找到指定的名字,则会抛出异常表明缺少这样的特性[^3]。 另外需要注意一点区别:虽然两者都涉及到了获取成员的操作,但是 `__getattribute__` 函数几乎适用于所有的属性检索过程;相比之下,`__getattr__` 只有在标准途径无法定位到目标的时候才被激活[^4]。 #### 单例模式中的应用举例 有时可以利用 `__getattr__` 来辅助构建某些设计模式下的解决方案,比如单例模式。尽管这里给出的例子主要依赖于装饰器完成任务,但我们同样能够借助类似的思路结合 `__getattr__` 达成目的[^5]: ```python class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: instance = super().__call__(*args, **kwargs) cls._instances[cls] = instance return cls._instances[cls] def __getattr__(cls, item): if hasattr(SingletonMeta._instances.get(cls), item): return getattr(SingletonMeta._instances.get(cls), item) raise AttributeError("No such attribute exists.") class MySingleton(metaclass=SingletonMeta): pass first_instance = MySingleton() second_instance = MySingleton() assert first_instance is second_instance ``` 在此案例中展示了另一种形式的单例实现方式——元编程技术。其中也体现了对于非正式存在的字段查询进行了重定向处理的方式[^6]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wanderer001

ROIAlign原理

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值