是否可以从在其定义表达式内部调用的内部函数访问类?
def called_inside_definition():
# I want to access 'Foo' here
class Foo:
called_inside_definition()
*这里的目标是为...提供替代语法 类装饰器:
@decorate
class Foo:
pass
我想提供一个装饰器来定义委托的方法/属性。
class Bar:
def grok(self):
return 'grok'
@delegate('bar', 'grok')
class Foo:
def __init__(self):
self.bar = Bar()
foo.grok()
# 'grok'
这很好用(要旨here)但是如果我要委托多个属性,那就有点难看了:
@delegate('bar', 'first, 'second', 'third', 'fourth')
class Foo:
...
所以,我想知道是否有可能在类定义本身内进行 。类似于Ruby语法:
class Foo
def_delegators :bar :grok
在课程之后 这样做不是最优的,因为读者会错过它。
delegate
的完整定义:
class Delegated(object):
def __init__(self, delegated_name, attr):
self.attr_name = attr
self.delegated_name = delegated_name
def __get__(self, instance, owner):
if instance is None:
return self
else:
return getattr(self.delegate(instance), self.attr_name)
def __set__(self, instance, value):
setattr(self.delegate(instance), self.attr_name, value)
def __delete__(self, instance):
delattr(self.delegate(instance), self.attr_name)
def delegate(self, instance):
return getattr(instance, self.delegated_name)
class delegate(object):
def __init__(self, src, *attrs):
self.src = src
self.attrs = attrs
def __call__(this, cls):
for attr in this.attrs:
setattr(cls, attr, Delegated(this.src, attr))
return cls
答案 0 :(得分:3)
class Foo:
called_inside_definition()
这时该类尚不存在,因此您无法访问它。该类是通过python调用元类(在这种情况下为type()
)创建的,在 之后,class Foo:
块完成。
答案 1 :(得分:1)
在Python中的类体内运行的代码无法修改类对象,因为该对象直到类体运行完毕才存在。但是,您可以在类名称空间中添加某些方法或变量,以某些方式控制其行为,或者可以由其他代码(例如装饰器或元类)使用这些方法或变量来操纵类或其名称空间。
最简单的方法是使用__getattr__
将某些属性查找映射到另一个对象。您可以编写一个函数为您生成__getattr__
方法:
def delegate_attrs(target, *names):
def __getattr__(self, name):
if name in names:
return getattr(getattr(self, target), name)
else:
raise AttributeError() # ideally we'd delegate to a superclass here, but we can't
return __getattr__
class Foo:
def __init__(self):
self.bar = Bar()
__getattr__ = delegate_attrs('bar', 'grok')
这样可以避免装饰器语法,在许多方面,它比您已经拥有的装饰器更糟糕。因为您需要在分配中显式命名__getattr__
,所以这有点尴尬,它不适用于dunder方法或处理设置或删除委托的属性。虽然您可以修复其中的某些问题(也许可以通过添加__setattr__
和__delattr__
方法来解决),但效果仍然不理想。
更好的解决方案可能是将装饰器语法(以便您可以操作类对象)与类内声明(该类声明应在何处委派)结合使用。例如,您可以将字典放入描述要委托的名称的类变量中:
@delegate # no arguments here!
class Foo:
delegate_names = {'bar': ['grok']} # could have more targets and/or names in the dict
def __init__(self):
self.bar = Bar()
更改使用这种类型的参数传递已经必须使用的delegate
装饰器将非常简单:
def delegate(cls):
for src, attrs in getattr(cls, "delegate_names", {}).items():
for attr in attrs:
setattr(cls, attr, Delegated(src, attr))
return cls
另一种方法是使用与上述相同的类变量,但使用元类而不是装饰器来完成设置描述符的工作。元类的优点是它可以被继承,而不是需要应用于每个类。如果您的类已经属于继承层次结构,则可以轻松添加(只需将元类显式添加到基类中即可)。
如果您只关心用引号和逗号来描述要委派的名称,则需要太多字符,那么您应该考虑允许用户传递单个字符串,该字符串将为split()
,以便空格可以分隔名称:@delegate("bar", "first second third fourth fifth")
。 namedtuple
类型工厂允许这种样式的名称传递。在当前代码中需要做的所有这些工作是在装饰类的__init__
方法的顶部添加以下内容:
if len(attrs) == 1:
attrs = attrs[0].split()