具体来说,我有一个类型为
的用户定义类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']
。有没有办法做到这一点?
答案 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)
这是不可能的。该类不拥有这些属性,只是设置它们的函数。因此,没有什么可以检索的,这是不可能的。
这只能通过深度AST检查才能实现。 Foo.bar.func_code
通常会在co_freevars
下拥有您想要的属性,但您在self
上查找属性,因此它们不是自由变量。您必须将字节码从func_code.co_code
反编译为AST,然后执行AST。
这是一个坏主意。无论你在做什么,都要找到一种不同的方式。
答案 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
对于那些认为这通常是个坏主意和/或不是你想要的人来说,我是记录在案的人。特别是,实例的寿命可能超出您的预期,具体取决于您传递它们的位置以及该代码对它们的作用,因此您可能并不总是拥有您认为具有的实例。 (其中一些甚至可能是隐式发生的。)通常最好明确这一点:不要让你的类的各种实例存储在整个代码(和库)的随机变量中,让它们的主存储库成为一个列表或其他容器,并从那里访问它们。然后,您可以轻松地迭代它们并获得您想要的任何属性。但是,可能有类似这样的用例,并且可以对其进行编码,所以我做了。