【问题标题】:Getting class reference from function in Python从 Python 中的函数获取类引用
【发布时间】:2013-01-04 16:12:03
【问题描述】:

我正在编写一个 python (3.2+) 插件库,我想创建一个函数来创建一些从配置文件自动处理的变量。

用例如下(类变量):

class X:
    option(y=0)
    def __init__(self):
        pass

(实例变量):

class Y:
    def __init__(self):
        option(y=0)

期权草案代码如下:

def option(**kwargs):
    frame   = inspect.stack()[1][0]
    locals_ = frame.f_locals
    if locals_ == frame.f_globals:
        raise SyntaxError('option() can only be used in a class definition')

    if '__module__' in locals_:
        # TODO

    else:
        for name, value in kwargs.items():
            if not name in locals_["self"].__class__.__dict__:
                setattr(locals_["self"].__class__, name, VirtualOption('_'+name, static=False))
            setattr(locals_["self"], '_'+name,value)

I have problem the first case, when option is declared as class variable.是否有可能以某种方式获得对使用此函数的类的引用(例如对类 X)?

【问题讨论】:

    标签: python introspection inspect


    【解决方案1】:

    您无法获得对该类的引用,因为该类尚未创建。你的父框架指向一个临时函数,它的locals() 完成后将用作类体。

    因此,您需要做的就是将变量添加到父框架局部变量中,这些将在类构建完成时添加到类中。

    简短演示:

    >>> def foo():
    ...     import sys
    ...     flocals = sys._getframe(1).f_locals
    ...     flocals['ham'] = 'eggs'
    ... 
    >>> class Bar:
    ...     foo()
    ... 
    >>> dir(Bar)
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__locals__', '__lt__', '__module__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'ham']
    >>> Bar.ham
    'eggs'
    

    【讨论】:

    • 这是一个非常明智的答案,但这真的是保证数据模型的工作方式吗? (答案可能是肯定的——这比我大多数时候需要探索的更深入地了解 Python 的内部工作原理)
    • @mgilson:是的。如果您真的关心,您可以检查构造类的函数的字节码 + ceval.c 循环,但这就是它的工作原理。
    • 我想我正在考虑其他 python 实现(Jythonpypy ...),其中ceval.c 可能完全不相关...
    • @mgilson:它是python specification 的一部分。 然后在新的执行框架中执行该类的套件
    • 谢谢,我知道了,但我想获得对类的引用。您已经说过,该类尚未创建,因此这是不可能的。谢谢你:)
    【解决方案2】:

    在我看来,元类在这里很合适:

    python2.x 语法

    def class_maker(name,bases,dict_):
        dict_['y']=0
        return type(name,bases,dict_)
    
    class X(object):
        __metaclass__ = class_maker
        def __init__(self):
            pass
    
    print X.y
    foo = X()
    print foo.y
    

    python3.x 语法

    似乎python3在类定义中使用了metaclass关键字:

    def class_maker(name,bases,dict_):
        dict_['y']=0
        return type(name,bases,dict_)
    
    class X(metaclass=class_maker):
        def __init__(self):
            pass
    
    print( X.y )
    foo = X()
    print( foo.y )
    print( type(foo) )
    

    或者,更符合您的问题:

    def class_maker(name,bases,dict_,**kwargs):
        dict_.update(kwargs)
        return type(name,bases,dict_)
    
    class X(metaclass=lambda *args: class_maker(*args,y=0)):
        def __init__(self):
            pass
    
    print( X.y )
    foo = X()
    print( foo.y )
    print( type(foo) )
    

    【讨论】:

    • 我不认为使用元类将正确处理实例用例,至少在元类中没有包装或以其他方式破解__init__ 时不会。我在想某种描述符可能会起作用,特别是因为他希望能够从调用上下文访问类。
    • @sr2222 -- 实例用例对我来说似乎很复杂。为什么不用self._option(**kwargs) 方法创建一个基类并在其中使用setattr
    • 确实如此,虽然我怀疑“为什么不只是”可能是一个长长的兔子洞来这里旅行。
    • @sr2222 -- 你可能是对的。无论如何,我对元类了解不多——我认为这是我第一次尝试使用元类来回答问题......这对我来说是一种学习体验:)
    • 不,不适合。正如 sr2222 所说,有一些特殊情况为什么我不想使用元类,是的,Python3+ 中的元类以其他方式处理(作为类定义的关键字参数)
    猜你喜欢
    • 2010-10-25
    • 2021-05-23
    • 2020-11-10
    • 1970-01-01
    • 2011-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多