可以访问类及其祖先的绑定方法上的装饰器
Posted
技术标签:
【中文标题】可以访问类及其祖先的绑定方法上的装饰器【英文标题】:Decorators on bound methods with access to class and his ancestors 【发布时间】:2017-02-03 12:20:37 【问题描述】:当我在 Python 类中装饰绑定方法时,我需要从外部类中获取此装饰器中的一些信息。这可能吗?
例如:
def modifier(func):
import sys
cls_namespace = sys._getframe(1).f_locals
cls_namespace['data'] # dictonary has no key 'data'
...
return func
class Parent:
data = "Hi!"
class Child(Parent):
@modifier
def method(self):
pass
cls_namespace
只是当前类的不完整命名空间,没有我需要获取的 data
字段。
有没有办法在装饰器中得到它?
【问题讨论】:
【参考方案1】:这是一个特别均匀的解决方案:
def wrap_class(cls):
"""Wrap a class to allow binding all decorated methods."""
for func in cls.__dict__.values():
if hasattr(func, '__wrapped__'):
func.__wrapped__.im_class = cls
return cls
要求你使用@functools.wraps(func)
,然后用@wraps_class
装饰类。
装饰器示例:
def decorator(func):
@wraps(func)
def inner(self, *args, **kwargs):
cls = self.__class__ # bound and straight-forward
...
def extra(*args, **kwargs):
cls = func.im_class # Not bound
...
inner.extra = extra
return inner
使用示例:
@wrap_class
class Badger:
@decorator
def stoat(self, mushroom, snake):
pass
Badger().stoat()
Badger.stoat.extra()
【讨论】:
【参考方案2】:函数修饰发生在执行类主体时,此时对类本身或其基类一无所知。这意味着modifier
装饰了未绑定的函数对象,并且只有在实例上实际调用func
时才会绑定它。
你可以返回一个包装函数来替换修饰函数,它会被绑定,你可以访问self
:
from functools import wraps
def modifier(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
# self is an instance of the class
self.data
return func(self, *args, **kwargs)
return wrapper
然后每次在实例上调用 method
时都会调用包装器。
如果您必须在创建类时访问基类,则必须等到class Child
语句完成执行。在 Python 3.6 之前,这只能通过类装饰器或元类实现; each 在类主体创建后调用,您将可以访问(至少)基类。
使用类装饰器,例如:
def modifier(cls):
# cls is the newly created class object, including class attributes
cls.data
return cls
@modifier
class Child(Parent):
def method(self):
pass
现在记下装饰器的位置。
Python 3.6 添加了一个__init_subclass__
method,它也可以为您提供访问权限;每次创建当前类的子类时都会调用 (class) 方法:
class Parent:
def __init_subclass__(cls, **kwargs):
# cls is a subclass of Parent
super().__init_subclass__(**kwargs)
cls.data
data = "Hi!"
class Child(Parent):
def method(self):
pass
【讨论】:
【参考方案3】:并不是说传递给装饰器包装器的第一个 arg 将是一个对象实例 (self
)。多亏了它,您可以访问 w=所有您需要的东西。
查看拉斐尔的回应: Related issue
def wrapped(self, *f_args, **f_kwargs):
self 是第一个参数
【讨论】:
以上是关于可以访问类及其祖先的绑定方法上的装饰器的主要内容,如果未能解决你的问题,请参考以下文章