【问题标题】:Setting a member variable string whose value is its name设置一个成员变量字符串,其值为它的名字
【发布时间】:2019-08-16 15:33:39
【问题描述】:

我想在我的代码中替换字符串文字,因为我想最大程度地减少拼写错误的风险,尤其是在 dict 键集中:

a['typoh'] = 'this is bad'
  • 我不想输入两次(值可能会出现错字)
  • 我希望它能够被各种 IDE “跟踪”(即单击 thru 以查看它的定义位置并转义完成)。
  • 枚举已失效:获取“a”的“E.a.name”是愚蠢的。

有人告诉我,这可以通过 slots 来完成,但如果没有一点小技巧,我不知道怎么做。我可以想到以下几种方法:

这是一个不可接受的答案:

class TwiceIsNotNice(object):
    this_is_a_string = 'this_is_a_string'
    ... (five thousand string constants in)
    this_has_a_hard_to_spot_typographical_error = 
                   'this_has_a_had_to_spot_typographical_error'
    ... (five thousand more string constants)

一个清晰但烦人的方法是使用“Stringspace”类/对象,其中属性是通过传入的字符串列表设置的。这解决了最小化的拼写风险,非常易于阅读,但既没有 IDE 可跟踪性也没有自动完成功能。没关系,但让人抱怨(请不要在这里抱怨,我只是在展示如何做到这一点):

string_consts = Stringspace('a', 'b',...,'asdfasdfasdf')
print(string_consts.a)

... where:
class Stringspace(object):
    def __init__(self, *strlist):
        for s in strlist:
            setattr(self, s, s)

另一种方法是使用哨兵对象定义一个类,在后期阶段设置值。这没关系,可跟踪,将自己呈现为实际类,允许别名等。但它需要在类结束时进行烦人的额外调用:

same = object()

class StrList(object):
    this_is_a_strval = same
    this_is_another_strval = same
    this_gets_aliased = "to something else"

# This would of course could become a function
for attr in dir(StrList):
    if getattr(StrList, attr) is same:
        setattr(StrList, attr, attr) 

print(StrList.a)

如果这就是所谓的 slot 魔法,那么我很失望,因为我们必须实际实例化一个对象:

class SlotEnum(object):
    __slots__ = []
    def __init__(self):
       for k in self.__slots__:
            setattr(self, k, k)

class Foo(SlotEnum):
    __slots__ = ['a', 'b']

foo_enum_OBJECT = Foo()
print(foo_enum_OBJECT.a)

【问题讨论】:

  • 也许我不明白这个问题,但听起来你在和风车搏斗。 IDE 提供代码完成功能,包括字典键,因此降低了拼写错误的风险。您还有其他替代字典的方法,例如命名元组或数据类。
  • @MarkGerolinatos:也许自定义元类可能是您可以接受的解决方案。请在下面检查我的答案。
  • 说什么?哪些 IDE 提供 dict 补全?严重地?指向我!

标签: python string enums slots


【解决方案1】:
  • 枚举已失效:E.a.name 获取 a 是愚蠢的。
from enum import Enum, auto

class StrEnum(str, Enum):
    "base class for Enum members to be strings matching the member's name"

    def __repr__(self):
        return '<%s.%s>' % (self.__class__.__name__, self.name)

    def __str__(self):
        return self.name


class E(StrEnum):

    a = auto()
    this_is_a_string = auto()
    no_typo_here = auto()
>>> print(repr(E.a))
<E.a>

>>> print(E.a)
a

>>> print('the answer is: %s!' % E.a)
the answer is: a!

【讨论】:

  • 刚看到这个,很酷。现在我们得弄清楚“auto”是什么。
【解决方案2】:

我找到了一个使用自定义元类的解决方案at this external link,用于包含字符串成员变量的类:

第 1 步,共 2 步:自定义元类可以这样定义:

class MetaForMyStrConstants(type):
    def __new__(metacls, cls, bases, classdict):
        object_attrs = set(dir(type(cls, (object,), {})))
        simple_enum_cls = super().__new__(metacls, cls, bases, classdict)
        simple_enum_cls._member_names_ = set(classdict.keys()) - object_attrs
        non_members = set()
        for attr in simple_enum_cls._member_names_:
            if attr.startswith('_') and attr.endswith('_'):
                non_members.add(attr)
            else:
                setattr(simple_enum_cls, attr, attr)

        simple_enum_cls._member_names_.difference_update(non_members)

        return simple_enum_cls

第 2 步,共 2 步: 定义字符串的类可以这样定义(使用虚拟值,例如空元组):

class MyStrConstants(metaclass=MetaForMyStrConstants):
    ONE_LONG_STR = ()
    ANOTHER_LONG_STR = ()
    THE_REAL_LONGEST_STR = ()

测试一下:

print (MyStrConstants.ONE_LONG_STR)
print (MyStrConstants.ANOTHER_LONG_STR)
print (MyStrConstants.THE_REAL_LONGEST_STR)

输出:

ONE_LONG_STR
ANOTHER_LONG_STR
THE_REAL_LONGEST_STR

【讨论】:

  • hmmmm...我想知道元类是否可以解决问题。我对它们并不完全熟悉,直到现在还没有发现它们的用途。
  • 元类的__new__() 方法将在解释器完成读取类(使用元类)的定义时被触发。我们只是利用该方法来动态设置属性。
猜你喜欢
  • 1970-01-01
  • 2015-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多