1

具体来说,我有一个用户定义的类型类

class Foo(object):
  def __init__(self, bar):
    self.bar = bar

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

我需要:

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

对于一个类的实例,这可以通过执行 a f = Foo('')and then来完成f.__dict__。有没有办法为一个类而不是一个实例做这件事?如果是,如何?我希望Foo.__dict__返回{'bar': None},但它不会以这种方式工作。

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

4

3 回答 3

3

假设我正确理解您正在尝试做的事情,这在动态语言中是相当困难的。本质上,这意味着遍历类的所有现有实例,然后收集这些实例上的所有设置属性。虽然并非不可行,但我会从设计和性能的角度质疑这种方法的实用性。

更具体地说,你说的是“类中定义的所有 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.

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

于 2013-09-25T12:46:40.767 回答
2
  1. 这是不可能的。该类没有这些属性,只有设置它们的函数。因此,没有什么可以检索的,这是不可能的。

  2. 这只有通过深度 AST 检查才能实现。Foo.bar.func_code通常会有你想要的属性,co_freevars但你正在查找属性self,所以它们不是自由变量。您将不得不将字节码从func_code.co_codeAST 反编译,然后步行说 AST。

这是一个坏主意。无论你在做什么,找到不同的方式去做。

于 2013-09-25T12:51:36.567 回答
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

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

于 2013-09-25T16:09:22.507 回答