gpt4 book ai didi

用于在类中注册标签的 Python Decorator

转载 作者:太空宇宙 更新时间:2023-11-03 14:36:34 25 4
gpt4 key购买 nike

我有一个类主要提供用于连接存储在字典中的数据的属性。每个属性(通常)负责字典中的一个键,但字典中可能还有其他键,这些键不受属性管理。

编辑 3:澄清一下,因为评论中有些困惑:

I have an arbitrary set of keys, for which I have a fixed subset of keys for which getters/setters are implemented, but the getters/setters are NOT identical in any meaningful way. I want a way to know for which keys I have defined such a getter/setter without having to maintain a separate list. It is more maintainable to just "tag" the getter/setter/property with the key it is responsible for and generate a list of managed keys from the tags. Also I hoped that I could then have a variable name in the getter/setter body that contains the property name, as to avoid typos.

大多数属性看起来像这样,但它们可能会执行更复杂的任务,例如转换数据类型

        @property
def name(self) -> str:
return self.header['MYKEY'] if 'MYKEY' in self.header else ""

@name.setter
def name(self, value: str):
self.header['MYKEY'] = value

我现在想要一个额外的装饰器 @RegisterProperty('MYKEY')

  1. 为 get/set 函数提供一个包含键名的附加参数(即装饰器的第一个参数),以避免拼写错误。类似于 @classmethod 将“隐藏”参数 cls 添加到函数的方式。
  2. 在一组包含属性的类中添加键(同样是装饰器的第一个参数),这样我就可以执行 myclass.registered_properties 来查看哪些键值是通过“管理”的属性,哪些不是。

从我读到的关于装饰器的内容来看,要走的路是在我想在其中使用它的类中定义一个基于类的装饰器(因为我不能在类中创建一个基于函数的装饰器,因为它会成为一种方法或其他东西)。我可以通过 *args,**kwargs 参数修改包装类方法(property getter setter)的签名,我也可以从那些中提取 self 来写myclass.registered_properties

但是

  1. 我在哪里可以访问装饰器的参数?
  2. 我如何“初始化”myclass.registered_properties,因为 self 仅在运行时可用,我不想每次都检查它是否存在(为了性能原因,主要是)。

Edit1:阅读后this我得到了这个装饰器原型(prototype):

class PropertyRegister:
def __init__(self, parent, property_name):
self.property_name = property_name
parent.registered_properties.add(property_name)

def __call__(self, func):
def wrapped_f(*args, **kwargs):
args.insert(0, self.property_name)
return func(*args, **kwargs)
functools.update_wrapper(self, func)
return wrapped_f

但仍然不确定如何确保parent.registered_properties 的存在。

Edit2:正如评论中所指出的,原型(prototype)装饰器不起作用,因为 self 不能传递给 parent装饰器,因为此时它还不存在。这是一个从函数调用本身拦截 self 的更新版本。但是有点丑。

    class PropertyRegister:
def __init__(self, property_name):
self.property_name = property_name

def __call__(self, func):
def wrapped_f(*args, **kwargs):
parent = args[0]
parent.registered_properties.add(self.property_name)
args.insert(1, self.property_name)
return func(*args, **kwargs)
functools.update_wrapper(self, func)
return wrapped_f

...

@RegisterProperty('MYKEY')
@property
def name(self, property_name) -> str:
return self.header[property_name] if property_name in self.header else ""

@RegisterProperty('MYKEY')
@name.setter
def name(self, property_name, value: str):
self.header[property_name] = value

问题在于,只有在实际调用 getter/setter 后,才会注册该属性。

最佳答案

这是一个 Q&D 可能的实现,它使用自定义属性类描述符和类装饰器来收集注册的键(您也可以使用自定义元类):

class FlaggedProperty():
def __init__(self, key):
self._key = key
self._getter = None
self._setter = None

def __call__(self, getter):
self._getter = getter
return self

def setter(self, setter):
self._setter = setter
return self

def __get__(self, obj, cls=None):
if obj is None:
return self
return self._getter(obj, self._key)

def __set__(self, obj, value):
if self._setter is None:
raise AttributeError("Attribute is read-only")
self._setter(obj, self._key, value)


def register_flagged_properties(cls):
cls.registered_properties = set()
for parent in cls.__mro__[1:]:
parent_properties = getattr(parent, "registered_properties", None)
if parent_properties:
cls.registered_properties.update(parent_properties)
for item in cls.__dict__.values():
if isinstance(item, FlaggedProperty):
cls.registered_properties.add(item._key)
return cls

以及它的使用方式:

@register_flagged_properties
class Foo():
def __init__(self, data):
self.data = data

@FlaggedProperty("foo")
def foo(self, key):
return self.data.get(key, None)

@foo.setter
def foo(self, key, value):
self.data[key] = value

I looked at the class creation process but I didn't get much out of it. Perhaps you are trying to point me at metaclasses?

好吧,首先,主要是,您必须了解如何在运行时创建类,以了解在流程的哪个阶段可用的内容。是的,元类应该是可能答案的一部分——连同 init_subclass Hook 或 class decorators (我的意思是:“应用于类的装饰器”,而不是“定义为类的装饰器”)。

The descriptor protocol just seems to cover the underlying mechanics of properties, but again I fail to see the pointer to the solution I seek.

它不仅由属性使用——它还(更重要的是)支持方法(实例方法和类方法)以及任何自定义描述符。

关于用于在类中注册标签的 Python Decorator,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57800468/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com