是否有Python'快捷方式'来定义一个等于其自己名称的字符串版本的类变量?

时间:2015-05-07 03:36:43

标签: python string variable-assignment

这有点愚蠢,但我想知道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')

上面将变量abc注入命名空间,定义为相应的sympy符号变量。

对于普通字符串,是否有类似的东西可以做到这一点?

class foo(object):
    [nifty thing]('bar', 'baz', 'baf')

编辑:要注意,我希望能够在使用foo的代码中将这些作为单独的标识符进行访问:

>>> f = foo(); print(f.bar)
bar

ADDENDUM:鉴于对问题的兴趣,我想我会提供更多关于我为什么要这样做的背景信息。我目前有两个用例:(1)一组自定义异常的类型代码(每个Exception子类都有一个不同的类型代码集); (2)轻量级枚举。我想要的功能集是:

  1. 只需在源定义中键入一次typecode / enum名称(或值)。 class foo(object): bar = 'bar'工作正常,但意味着我必须在源代码中输入两次,这会让更长的名字变得烦人并暴露出错字风险。
  2. 为IDE自动完成公开的有效typecodes / enum值。
  3. 内部存储为可理解字符串的值:

    1. 对于Exception子类,我希望能够将myError.__str__定义为类似return self.typecode + ": " + self.message + " (" + self.source + ")"的内容,而无需执行大量dict - fu to将intself.typecode反向引用为可理解且有意义的字符串。
    2. 对于枚举,我希望能够从widget获得e = myEnum.widget; print(e)作为输出,同样没有很多dict - fu。

      • 我知道这会增加开销。我的应用程序不是速度敏感的(基于GUI的工具用于驱动单独的程序),所以我认为这根本不重要。
  4. 直接的成员资格测试,还包括(比方说)包含所有类型代码/枚举字符串值的frozenset作为myError.typecodes / myEnum.E类。这通过简单的健全性检查(如if not enumVal in myEnum.E: raise(ValueError('Invalid enum value: ' + str(enumVal))))来解决意外(或故意......但为什么?!)使用无效的类型代码/枚举字符串的潜在问题。

  5. 能够通过,例如from errmodule import squirrelerror导入单个枚举/异常子类,以避免使用不相关的异常子类混淆使用环境的命名空间。我相信这禁止任何需要在模块级别上进行后期修改的解决方案,就像Sinux提出的那样。
  6. 对于枚举用例,我宁愿避免引入额外的包依赖,因为我(我认为)并不关心官方enum类中可用的任何额外功能。无论如何,它仍然无法解决#1。
  7. 我已经想出了我对上述所有内容的满意度,但#1。我对#1解决方案的兴趣(不破坏其他解决方案)部分是为了防止将typecode / enum值输入源代码,以及部分普通的懒惰。 (那个刚刚在这个主题上输入一个巨大的SO问题的家伙。)

7 个答案:

答案 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)来更改继承。