y3t*_*3t1 3 python metaclass decorator python-3.x python-decorators
我想防止实例的用户在使用所述对象时错误地创建不存在的属性。
假设我有一个带有 init 和一些属性的类:
class Foo(obj):
def __init__(self, a, b):
self.a = a
self.b = b
Run Code Online (Sandbox Code Playgroud)
我想启用设置现有属性,同时防止创建新属性:
myobj = foo(1,2)
>>
print(myobj.a)
>> 1
myobj.a = 2
myobj.c = 1
>> AttributeError: frozen instance
Run Code Online (Sandbox Code Playgroud)
__setattr__使用override 和 boolean就相当容易了:
class Foo(obj):
_is_frozen = False
def __init__(self, a, b):
self.a = a
self.b = b
self._is_frozen = True
def __setattr__(self, name, value):
if not self._is_frozen or hasattr(self, name):
super().__setattr__(name, value)
else:
raise AttributeError("frozen instance")
Run Code Online (Sandbox Code Playgroud)
现在我遇到的困难是当一个新类继承 Foo 时。如果在调用 后必须定义新属性super().__init__(),则实例将被冻结。
我尝试使用装饰器来创建元类,但母类的装饰器仍然在调用时被调用super().__init__(),并且我无法定义我的新属性。
换句话说,是否有一种方法可以创建一种__post_init__方法(引用 dataclass 模块),该方法仅在调用所有 init(类和继承类的初始化)后才被调用?
确实 - 这很棘手。
在 基类上有一个装饰器__init__,它会冻结之后的实例__init__,正如您提到的,存在状态问题 - 可以添加其他状态变量,或计算深度 __init__,并使用元类(或__init_subclass__)来装饰__init__中的所有方法子类,以便仅在退出最外层时才进行冻结__init__。
但是使用元类有一种更简单的方法:元类的__call__方法是调用类的方法__new__,然后__init__在创建新实例时调用。因此,只需放置代码来调用__post_init__自定义元类__call__(或直接从中冻结实例)
class PostInitMeta(type):
def __call__(cls, *args, **kw):
instance = super().__call__(*args, **kw) # < runs __new__ and __init__
instance.__post_init__()
return instance
class Freezing(metaclass=PostInitMeta):
_frozen = False
def __post_init__(self):
self._frozen = True
def __setattr__(self, name, value):
if self._frozen:
raise AttributeError() # or do nothing, and return as you prefer
super().__setattr__(name, value)
class A(Freezing):
def __init__(self, a):
self.a = a
class B(A):
def __init__(self, a, b):
super().__init__(a)
self.b = b
Run Code Online (Sandbox Code Playgroud)
并在交互模式下测试:
In [18]: b = B(2, 3)
In [19]: b.a
Out[19]: 2
In [20]: b.b
Out[20]: 3
In [21]: b.b = 5
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In [21], line 1
----> 1 b.b = 5
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
466 次 |
| 最近记录: |