这有点愚蠢,但我想知道Python中是否有简洁的方法来定义包含自己名字的字符串表示的类变量。例如,可以定义:
class foo(object):
bar = 'bar'
baz = 'baz'
baf = 'baf'
根据消耗的线条写一个更简洁的方法可能是:
class foo(object):
bar, baz, baf = 'bar', 'baz', 'baf'
即使在那里,我仍然需要两次输入每个标识符,一次分配一次,并且错别字的机会很多。
我想要的是sympy
方法var
提供的内容:
sympy.var('a,b,c')
上面将变量a
,b
和c
注入命名空间,定义为相应的sympy
符号变量。
对于普通字符串,是否有类似的东西可以做到这一点?
class foo(object):
[nifty thing]('bar', 'baz', 'baf')
编辑:要注意,我希望能够在使用foo
的代码中将这些作为单独的标识符进行访问:
>>> f = foo(); print(f.bar)
bar
ADDENDUM:鉴于对问题的兴趣,我想我会提供更多关于我为什么要这样做的背景信息。我目前有两个用例:(1)一组自定义异常的类型代码(每个Exception
子类都有一个不同的类型代码集); (2)轻量级枚举。我想要的功能集是:
class foo(object): bar = 'bar'
工作正常,但意味着我必须在源代码中输入两次,这会让更长的名字变得烦人并暴露出错字风险。内部存储为可理解字符串的值:
Exception
子类,我希望能够将myError.__str__
定义为类似return self.typecode + ": " + self.message + " (" + self.source + ")"
的内容,而无需执行大量dict
- fu to将int
值self.typecode
反向引用为可理解且有意义的字符串。对于枚举,我希望能够从widget
获得e = myEnum.widget; print(e)
作为输出,同样没有很多dict
- fu。
直接的成员资格测试,还包括(比方说)包含所有类型代码/枚举字符串值的frozenset
作为myError.typecodes
/ myEnum.E
类。这通过简单的健全性检查(如if not enumVal in myEnum.E: raise(ValueError('Invalid enum value: ' + str(enumVal)))
)来解决意外(或故意......但为什么?!)使用无效的类型代码/枚举字符串的潜在问题。
from errmodule import squirrelerror
导入单个枚举/异常子类,以避免使用不相关的异常子类混淆使用环境的命名空间。我相信这禁止任何需要在模块级别上进行后期修改的解决方案,就像Sinux提出的那样。enum
类中可用的任何额外功能。无论如何,它仍然无法解决#1。我已经想出了我对上述所有内容的满意度,但#1。我对#1解决方案的兴趣(不破坏其他解决方案)部分是为了防止将typecode / enum值输入源代码,以及部分普通的懒惰。 (那个刚刚在这个主题上输入一个巨大的SO问题的家伙。)
答案 0 :(得分:5)
我建议使用collections.namedtuple
:
示例:强>
>>> from collections import namedtuple as nifty_thing
>>> Data = nifty_thing("Data", ["foo", "bar", "baz"])
>>> data = Data(foo=1, bar=2, baz=3)
>>> data.foo
1
>>> data.bar
2
>>> data.baz
3
旁注:如果您在Python 3.x上使用/,我建议{@ 3}}根据@ user2357112的评论。这是Python 3 +
的标准化方法 更新好的,如果我理解OP的完全要求,我认为这是实现这一目标的唯一方法(并且可能sympy
这样做)是将名称/变量注入globals()
或locals()
命名空间。例如:
#!/usr/bin/env python
def nifty_thing(*names):
d = globals()
for name in names:
d[name] = None
nifty_thing("foo", "bar", "baz")
print foo, bar, bar
输出:
$ python foo.py
None None None
NB: 我真的不推荐这个!:)
更新#2:您在问题中展示的另一个示例是这样实现的:
#!/usr/bin/env python
import sys
def nifty_thing(*names):
frame = sys._getframe(1)
locals = frame.f_locals
for name in names:
locals[name] = None
class foo(object):
nifty_thing("foo", "bar", "baz")
f = foo()
print f.foo, f.bar, f.bar
输出:
$ python foo.py
None None None
NB:这受zope.interface.implements()
启发。
答案 1 :(得分:4)
current_list = ['bar', 'baz', 'baf']
class foo(object):
"""to be added"""
for i in current_list:
setattr(foo, i, i)
然后运行:
>>>f = foo()
>>>print(f.bar)
bar
>>>print(f.baz)
baz
答案 2 :(得分:3)
这与您要求的完全不同,但它似乎应该完成这项工作:
class AutoNamespace(object):
def __init__(self, names):
try:
# Support space-separated name strings
names = names.split()
except AttributeError:
pass
for name in names:
setattr(self, name, name)
演示:
>>> x = AutoNamespace('a b c')
>>> x.a
'a'
如果你想做SymPy对var
做的事情,你可以,但我强烈建议不要这样做。也就是说,这是一个基于sympy.var
的源代码的函数:
def var(names):
from inspect import currentframe
frame = currentframe().f_back
try:
names = names.split()
except AttributeError:
pass
for name in names:
frame.f_globals[name] = name
演示:
>>> var('foo bar baz')
>>> bar
'bar'
它总是会创建全局变量,即使你从函数或类中调用它也是如此。 inspect
用于获取调用者的全局变量,而globals()
将获得var
自己的全局变量。
答案 3 :(得分:0)
如何将变量定义为emtpy字符串,然后获取其名称:
class foo(object):
def __getitem__(self, item):
return item
foo = foo()
print foo['test']
答案 4 :(得分:0)
这是bman想法的延伸。这有其优点和缺点,但至少它可以与一些自动完成器一起使用。
class FooMeta(type):
def __getattr__(self, attr):
return attr
def __dir__(self):
return ['bar', 'baz', 'baf']
class foo:
__metaclass__ = FooMeta
这允许对所有foo.xxx
进行'xxx'
→xxx
访问,还可以通过__dir__
指导自动填充。
答案 5 :(得分:0)
弄清楚我在寻找什么:
>>> class tester:
... E = frozenset(['this', 'that', 'the', 'other'])
... for s in E:
... exec(str(s) + "='" + str(s) + "'") # <--- THIS
...
>>> tester()
<__main__.tester instance at 0x03018BE8>
>>> t = tester()
>>> t.this
'this'
>>> t.that in tester.E
True
只需要定义一次元素字符串,我非常确定它可以满足问题中列出的所有要求。在实际实现中,我计划将str(s) + "='" + str(s) + "'"
封装在辅助函数中,这样我就可以在exec(helper(s))
循环中调用for
。 (我非常确定exec
必须放在类的主体中,而不是放在辅助函数中,否则新的变量将被注入到辅助函数的(临时)范围内,而不是班上的。)
编辑:经过详细测试后,这不起作用 - 使用exec
可防止IDE内省了解已创建变量的存在。
答案 6 :(得分:0)
我认为你可以使用元类来实现一个相当漂亮的解决方案,但是我不能流利地使用它来作为答案,但我确实有一个选项似乎工作得很好:
def new_enum(name, *class_members):
"""Builds a class <name> with <class_members> having the name as value."""
return type(name, (object, ), { val : val for val in class_members })
Foo = new_enum('Foo', 'bar', 'baz', 'baf')
这应该重新创建您作为示例给出的类,如果需要,可以通过将调用的第二个参数更改为class type
(name, bases, dict)来更改继承。