繁体   English   中英

在类定义表达式内部调用的函数内部的访问类

[英]Access class inside function that was called inside class definition expression

是否可以从在其定义表达式内部调用的内部函数访问类?

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'

这很好用(要点在这里 ), 但是如果我委托了多个属性,它将变得有点难看:

@delegate('bar', 'first, 'second', 'third', 'fourth')
class Foo:
    ...

所以,我想知道是否有可能类定义本身中做到这一点。 类似于Ruby语法:

class Foo
    def_delegators :bar :grok

课后做起来unoptimal因为读者比错过。

完整的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
class Foo:
    called_inside_definition()

此时该类尚不存在,因此您无法访问它。 class Foo:块完成之后 ,通过python调用元类(在这种情况下为type()来创建class Foo:

在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__显式命名__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()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM