关于python:类和函数范围

Class and function scope

我想从用户输入的字符串中创建类的实例,所以我使用了exec()函数。问题是,我无法通过函数外部的实例名称访问它。我的第一个想法是,这是一个函数范围的问题,我仍然认为这是问题,但当我把实例放在一个列表中时,我可以访问它们,只是不使用它们的名称。我不太确定这里发生了什么……有没有一种方法可以让我以实例的名称访问实例,比如thing1.properties,但是在函数之外,因为这不是我的整个代码,所以把所有的东西都放在函数之外会很混乱?类似于在函数中创建实例列表,并"提取"函数外部的所有实例,以便我可以在函数外部访问它们。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
class Things:
    def __init__(self, properties):
        self.properties = properties

listt = []
def create_instance():
    exec("thing1=Things('good')")
    listt.append(thing1)

create_instance()
print listt[0].properties
print thing1.properties


虽然我讨厌污染全局命名空间,但exec语句可以使用第二个参数作为作用域,默认值为locals()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> def foo(name):
...     exec"{} = 1".format(name)
...
>>> def bar(name):
...     exec"{} = 1".format(name) in globals()
...
>>> foo('a')
>>> a
Traceback (most recent call last):
File"<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> bar('a')
>>> a
1

所以,如果您通过globals作为范围,它将按您的需要工作,但真的吗?污染全球范围本身是可怕的,在评估用户提供的代码时这样做是一个该死的责任。

[更新]

Very helpful! Thank you! But what is now better way of doing it, dictionary or a global scope?

也许您可以将所有实例存储到一个类变量中,例如:

1
2
3
4
5
6
7
8
9
class Thing(object):
    instances = {}
    def __init__(self, name, **properties):
        self.name = name
        self.properties = properties
        self.instances[name] = self
    def __repr__(self):
        t = '<"{self.name}" thing, {self.properties}>'
        return t.format(self=self)

现在你可以做到:

1
2
3
4
5
6
7
8
9
10
11
12
13
# declare your things
>>> Thing('foo', a=1, b=2)
>>> Thing('bar', a=3, b=4)

# retrieve them by name
>>> Thing.instances.get('foo')
<"foo" thing, {'a': 1, 'b': 2}>

>>> Thing.instances.get('foo').properties
{'a': 1, 'b': 2}

>>> Thing.instances.get('bar').properties
{'a': 3, 'b': 4}