有没有Python'shortcut'定义一个class变量等于自己名字的字符串版本?
Is there a Python 'shortcut' to define a class variable equal to a string version of its own name?
这有点傻,但我想知道 Python 中是否有简洁的方法来定义包含自己名称的字符串表示的 class 变量。例如,可以定义:
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
附录: 鉴于对这个问题的兴趣,我想我会提供更多关于我为什么要这样做的背景信息。我目前有两个用例:(1)一组自定义异常的类型代码(每个 Exception
subclass 都有一个不同的类型代码集); (2) 轻量级枚举。我想要的功能集是:
- 只需在源定义中键入一次类型代码/枚举名称(或值)。
class foo(object): bar = 'bar'
工作正常,但意味着我必须在源代码中输入两次,这对较长的名称来说很烦人,并且有打字错误的风险。
- 为 IDE 自动完成公开的有效类型代码/枚举值。
内部存储为可理解字符串的值:
- 对于
Exception
subclasses,我希望能够将 myError.__str__
定义为类似于 return self.typecode + ": " + self.message + " (" + self.source + ")"
的东西,而不必做很多事情dict
-fu 将 self.typecode
的 int
值反向引用为可理解且有意义的字符串。
对于枚举,我希望能够从 e = myEnum.widget; print(e)
获得 widget
作为输出,同样不需要很多 dict
-fu。
- 我知道这会增加开销。我的应用程序对速度不敏感(用于驱动单独程序的基于 GUI 的工具),所以我认为这根本不重要。
直接的成员资格测试,通过还包括(比如)一个包含所有类型代码/枚举字符串值的 frozenset
作为 myError.typecodes
/myEnum.E
class是的。这解决了通过 if not enumVal in myEnum.E: raise(ValueError('Invalid enum value: ' + str(enumVal)))
.
等简单完整性检查意外(或故意……但为什么呢?!)使用无效类型代码/枚举字符串的潜在问题
- 能够通过
from errmodule import squirrelerror
导入单个枚举/异常子classes,以避免使用不相关的异常子classes使使用环境的命名空间混乱.我相信这会禁止任何需要 post-twiddling 的解决方案,就像 Sinux 提出的那样。
- 对于枚举用例,我宁愿避免引入额外的包依赖性,因为我(认为我)不关心官方
enum
class 中可用的任何额外功能。无论如何,它仍然无法解决#1。
除了#1 之外,我已经找到了令我满意的所有上述实施方式。我对 #1 的解决方案(不破坏其他解决方案)的兴趣部分是希望将类型代码/枚举值输入到源代码中以防止打字错误,部分是因为懒惰。 (刚刚输入关于该主题的巨大 SO 问题的人说。)
如何将变量定义为空字符串,然后获取它们的名称:
class foo(object):
def __getitem__(self, item):
return item
foo = foo()
print foo['test']
我推荐使用 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
旁注: 如果你是 using/on Python 3.x 我会根据@user2357112 的评论推荐 Enum .这是 Python 3+
的标准化方法
更新: 好吧,如果我在这里理解 OP 的 确切 要求,我认为唯一的方法是做到这一点( 大概 sympy
也这样做 ) 是将 names/variables 注入 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
注意: 我真的不推荐这个! :)
更新 #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
注意: 这是受 zope.interface.implements()
.
的启发
这与您要求的不完全一样,但它似乎应该可以完成工作:
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'
它将始终创建全局变量,即使您从函数或 class 内部调用它也是如此。 inspect
用于获取调用者的全局变量,而 globals()
将获取 var
自己的全局变量。
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
这里是 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__
引导自动完成。
找出我要找的东西:
>>> 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) + "'"
封装在一个辅助函数中,这样我就可以在for
循环中调用exec(helper(s))
。 (我很确定 exec
必须放在 class 的主体中,而不是放在辅助函数中,否则新变量将被注入到辅助函数,不是 class.)
的辅助函数
编辑: 经过详细测试,这不起作用——exec
的使用阻止了 IDE 的内省知道存在创建的变量。
我认为你可以使用 metaclasses 实现一个相当漂亮的解决方案,但我不够流利地使用它们来呈现它作为答案,但我确实有一个似乎可行的选项相当不错:
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,如果您愿意,可以通过将调用的第二个参数更改为 class type
(name, bases, dict) 来更改继承。
这有点傻,但我想知道 Python 中是否有简洁的方法来定义包含自己名称的字符串表示的 class 变量。例如,可以定义:
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
附录: 鉴于对这个问题的兴趣,我想我会提供更多关于我为什么要这样做的背景信息。我目前有两个用例:(1)一组自定义异常的类型代码(每个 Exception
subclass 都有一个不同的类型代码集); (2) 轻量级枚举。我想要的功能集是:
- 只需在源定义中键入一次类型代码/枚举名称(或值)。
class foo(object): bar = 'bar'
工作正常,但意味着我必须在源代码中输入两次,这对较长的名称来说很烦人,并且有打字错误的风险。 - 为 IDE 自动完成公开的有效类型代码/枚举值。
内部存储为可理解字符串的值:
- 对于
Exception
subclasses,我希望能够将myError.__str__
定义为类似于return self.typecode + ": " + self.message + " (" + self.source + ")"
的东西,而不必做很多事情dict
-fu 将self.typecode
的int
值反向引用为可理解且有意义的字符串。 对于枚举,我希望能够从
e = myEnum.widget; print(e)
获得widget
作为输出,同样不需要很多dict
-fu。- 我知道这会增加开销。我的应用程序对速度不敏感(用于驱动单独程序的基于 GUI 的工具),所以我认为这根本不重要。
- 对于
直接的成员资格测试,通过还包括(比如)一个包含所有类型代码/枚举字符串值的
frozenset
作为myError.typecodes
/myEnum.E
class是的。这解决了通过if not enumVal in myEnum.E: raise(ValueError('Invalid enum value: ' + str(enumVal)))
. 等简单完整性检查意外(或故意……但为什么呢?!)使用无效类型代码/枚举字符串的潜在问题
- 能够通过
from errmodule import squirrelerror
导入单个枚举/异常子classes,以避免使用不相关的异常子classes使使用环境的命名空间混乱.我相信这会禁止任何需要 post-twiddling 的解决方案,就像 Sinux 提出的那样。 - 对于枚举用例,我宁愿避免引入额外的包依赖性,因为我(认为我)不关心官方
enum
class 中可用的任何额外功能。无论如何,它仍然无法解决#1。
除了#1 之外,我已经找到了令我满意的所有上述实施方式。我对 #1 的解决方案(不破坏其他解决方案)的兴趣部分是希望将类型代码/枚举值输入到源代码中以防止打字错误,部分是因为懒惰。 (刚刚输入关于该主题的巨大 SO 问题的人说。)
如何将变量定义为空字符串,然后获取它们的名称:
class foo(object):
def __getitem__(self, item):
return item
foo = foo()
print foo['test']
我推荐使用 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
旁注: 如果你是 using/on Python 3.x 我会根据@user2357112 的评论推荐 Enum .这是 Python 3+
的标准化方法更新: 好吧,如果我在这里理解 OP 的 确切 要求,我认为唯一的方法是做到这一点( 大概 sympy
也这样做 ) 是将 names/variables 注入 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
注意: 我真的不推荐这个! :)
更新 #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
注意: 这是受 zope.interface.implements()
.
这与您要求的不完全一样,但它似乎应该可以完成工作:
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'
它将始终创建全局变量,即使您从函数或 class 内部调用它也是如此。 inspect
用于获取调用者的全局变量,而 globals()
将获取 var
自己的全局变量。
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
这里是 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__
引导自动完成。
找出我要找的东西:
>>> 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) + "'"
封装在一个辅助函数中,这样我就可以在for
循环中调用exec(helper(s))
。 (我很确定 exec
必须放在 class 的主体中,而不是放在辅助函数中,否则新变量将被注入到辅助函数,不是 class.)
编辑: 经过详细测试,这不起作用——exec
的使用阻止了 IDE 的内省知道存在创建的变量。
我认为你可以使用 metaclasses 实现一个相当漂亮的解决方案,但我不够流利地使用它们来呈现它作为答案,但我确实有一个似乎可行的选项相当不错:
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,如果您愿意,可以通过将调用的第二个参数更改为 class type
(name, bases, dict) 来更改继承。