【问题标题】:Improving my `namedlist` class inspired by `namedtuple`受 `namedtuple` 启发,改进我的 `namedlist` 类
【发布时间】:2015-11-02 00:46:10
【问题描述】:

背景

我需要一个类似于namedtuple 的类型,但具有可更改 字段。 namedlist 的行为如下:

Something = namedlist('Something', ['a', 'b'])
st = Something(123, 456)
print st                 # Something(a=123, b=456)
a, b = st
print a, b, st.a, st.b   # 123 456 123 456

st.a = 777
print st                 # Something(a=777, b=456)
st.b = 888
print st                 # Something(a=777, b=888)

在了解了描述符 herethere 之后,我得到了以下代码,如上所述:

class IndexAccessor(object):
    def __init__(self, index):
        self.index = index
    def __get__(self, instance, owner):
        return list.__getitem__(instance, self.index)
    def __set__(self, instance, value):
        list.__setitem__(instance, self.index, value)

def namedlist(classname, fields):
    def init(self, *args):
        list.__init__(self, args)
    def repr(self):
        return '%s(%s)' % (self.__class__.__name__, ', '.join(['%s=%s' % (k,list.__getitem__(self, i)) for i, k in enumerate(self._fields)]))
    attrs = {'_fields': tuple(fields),
             '__init__': init,
             '__repr__': repr
             }        
    for index, field in enumerate(fields):
        attrs[field] = IndexAccessor(index)
    return type(classname, (list,), attrs)

问题

IndexAccessor 类对我来说有点像样板,所以我想知道是否可以改进代码以避免它。

我试图用这个替换 for index, field in enumerate(fields) 循环

for index, field in enumerate(fields):
    def get(self): 
        return list.__getitem__(self, index)
    def set(self, v):
        list.__setitem__(self, index, v)
    attrs[field] = property(get, set, None, 'Property "%s" mapped to item %d' % (field, index))

...但这导致print a, b, st.a, st.b 语句生成123 456 456 456 而不是123 456 123 456,我怀疑这与index 没有按要求工作有关。

奖励积分对于答案也可以提供简洁的代码以允许在构造函数中进行基于名称的分配,如

Something = namedlist('Something', ['a', 'b'])
st = Something(a=123, b=456)

【问题讨论】:

  • 采用namedtuple 本身所做的方法,您在namedlist 中创建一个类模板字符串,将给定字段应用于模板,然后exec 生成的字符串生成实际的类。
  • 如果这是您认为可以改进的工作代码,请参阅Code Review。如果不是,请澄清问题。
  • @jonrsharpe 我不同意,因为如果 namedlist 的工作版本被删除,这个问题仍然有意义。
  • @jonrsharpe:后台的代码确实可以工作,除了构造中基于名称的分配。我还可以删除IndexAccessor 类并替换问题中描述的for 循环,这会破坏代码。这比将问题移至代码审查更可取吗?

标签: python python-2.7 properties namedtuple python-descriptors


【解决方案1】:

for循环不起作用的原因是getset函数中引用的index变量没有被复制。它是对在 for 循环中设置的 index 变量的引用。所以所有的属性访问都会得到最后一个值。这可以通过将索引显式存储为默认参数来解决。

固定代码:

for index, field in enumerate(fields):
    def get(self,index=index): 
        return list.__getitem__(self, index)
    def set(self, v,index=index):
        list.__setitem__(self, index, v)
    attrs[field] = property(get, set, None, 'Property "%s" mapped to item %d' % (field, index))

要允许关键字参数,请将您的 __init__ 函数修改为以下内容:

def init(self, *args, **kwargs):
    kwargs.update(zip(self._fields, args))
    args = [kwargs[k] for k in self._fields]
    list.__init__(self, args)

【讨论】:

    【解决方案2】:

    我采取了不同的方法来解决这个问题,这可能值得与您一起发布。它更通用(可能需要也可能不需要)而且更短,但也可能效率低下。

    class Struct:
        def __init__(self):
            pass
        def __getattribute__(self, key):
            return super().__getattribute__(key)
        def __setattribute__(self, key, value):
            super().__setattribute__(key, value)
    

    【讨论】:

      猜你喜欢
      • 2016-06-14
      • 1970-01-01
      • 2020-05-31
      • 1970-01-01
      • 1970-01-01
      • 2017-11-01
      • 2018-11-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多