如何从python类函数访问self的调用

时间:2013-09-25 12:40:44

标签: python class metaprogramming self

具体来说,我有一个类型为

的用户定义类
class Foo(object):
  def __init__(self, bar):
    self.bar = bar

  def bind(self):
    val = self.bar
    do_something(val)

我需要:

1)能够调用类(不是类的实例)来恢复类中定义的所有self.xxx属性。

对于类的实例,可以通过执行f = Foo('')然后f.__dict__来完成。有没有办法为一个类而不是一个实例?如果有,怎么样?我希望Foo.__dict__返回{'bar': None},但它不会这样。

2)能够访问从类的特定函数调用的所有self.xxx参数。例如,我想做Foo.bind.__selfparams__并收到回复['bar']。有没有办法做到这一点?

3 个答案:

答案 0 :(得分:3)

这是动态语言中很难做到的事情,假设我正确地理解了你想要做的事情。从本质上讲,这意味着遍历该类存在的所有实例,然后收集这些实例上的所有set属性。虽然不可行,但我从设计和绩效的角度来质疑这种方法的实用性。

更具体地说,你在谈论“在类中定义的所有self.xxx属性” - 但这些东西根本没有定义,至少在一个地方也没有定义 - 它们更像是“进化”更多和更多的班级实例得以实现。现在,我并不是说你的所有实例都设置了不同的属性,但它们可能会,并且为了获得可靠的通用解决方案,你必须跟踪实例可能对自己做的任何事情。因此,除非您考虑到静态分析方法,否则我看不到一种干净有效的方法来实现它(实际上,即使静态分析在动态语言中也没有帮助)。

证明我的观点的一个简单例子:

class Foo(object):
    def __init__(self):
        # statically analysable
        self.bla = 3

        # still, but more difficult
        if SOME_CONSTANT > 123:
            self.x = 123
        else:
            self.y = 321

    def do_something(self):
        import random
        setattr(self, "attr%s" % random.randint(1, 100), "hello, world of dynamic languages!")

    foo = Foo()
    foo2 = Foo()
    # only `bla`, `x`, and `y` attrs in existence so far
    foo2.do_something()
    # now there's an attribute with a random name out there
    # in order to detect it, we'd have to get all instances of Foo existence at the moment, and individually inspect every attribute on them.

而且,即使您要迭代现有的所有实例,您也只会获得您感兴趣的快照,而不是所有可能的属性。

答案 1 :(得分:2)

  1. 这是不可能的。该类不拥有这些属性,只是设置它们的函数。因此,没有什么可以检索的,这是不可能的。

  2. 这只能通过深度AST检查才能实现。 Foo.bar.func_code通常会在co_freevars下拥有您想要的属性,但您在self上查找属性,因此它们不是自由变量。您必须将字节码从func_code.co_code反编译为AST,然后执行AST。

  3. 这是一个坏主意。无论你在做什么,都要找到一种不同的方式。

答案 2 :(得分:0)

为此,您需要一些方法来查找班级的所有实例。一种方法是让类本身跟踪其实例。不幸的是,保持对类中每个实例的引用意味着这些实例永远不会被垃圾回收。幸运的是,Python有weakref,它将保留对对象的引用,但不算作对Python内存管理的引用,因此实例可以按照惯例进行垃圾收集。

更新实例列表的好地方在于__init__()方法。如果您发现关注点分离得更清楚,您也可以在__new__()中执行此操作。

import weakref

class Foo(object):

    _instances = []

    def __init__(self, value):
        self.value = value

        cls = type(self)
        type(self)._instances.append(weakref.ref(self, 
                                                 type(self)._instances.remove))

    @classmethod
    def iterinstances(cls):
        "Returns an iterator over all instances of the class."
        return (ref() for ref in cls._instances)

    @classmethod
    def iterattrs(cls, attr, default=None):
        "Returns an iterator over a named attribute of all instances of the class."
        return (getattr(ref(), attr, default) for ref in cls._instances)

现在你可以这样做:

f1, f2, f3 = Foo(1), Foo(2), Foo(3)

for v in Foo.iterattrs("value"):
    print v,   # prints 1 2 3

对于那些认为这通常是个坏主意和/或不是你想要的人来说,我是记录在案的人。特别是,实例的寿命可能超出您的预期,具体取决于您传递它们的位置以及该代码对它们的作用,因此您可能并不总是拥有您认为具有的实例。 (其中一些甚至可能是隐式发生的。)通常最好明确这一点:不要让你的类的各种实例存储在整个代码(和库)的随机变量中,让它们的主存储库成为一个列表或其他容器,并从那里访问它们。然后,您可以轻松地迭代它们并获得您想要的任何属性。但是,可能有类似这样的用例,并且可以对其进行编码,所以我做了。