类似命名元组的类
我发现自己在写Python代码时,经常需要一个快速使用的类,所以我常常写这个类。
class Struct(object):
def __init__( self, **kwargs ):
for k in kwargs:
setattr(self,k,kwargs[k])
基本的想法是,我可以像这样快速做一些事情:
foo = Struct( bar='one', baz=1 )
print foo.bar
foo.baz += 1
foo.novo = 42 # I don't do this as often.
当然,这种方式不太适合大规模使用,添加方法也会变得很麻烦,但即便如此,我还是有很多只用一次的数据类,所以我一直在用它。
我原以为namedtuple会是这样的东西。但是namedtuple的语法太复杂了,不太好用。
在标准库里有没有我还没发现的,能做到这个或者更好的东西呢?
这样做算不算坏风格?或者说它有什么隐藏的问题吗?
更新
这里有两个具体的例子,说明为什么我不直接用字典。这两个例子可以用字典来做,但显然不太符合常规做法。
#I know an order preserving dict would be better but they don't exist in 2.6.
closure = Struct(count=0)
def mk_Foo( name, path ):
closure.count += 1
return (name, Foo( name, path, closure.count ))
d = dict([
mk_Foo( 'a', 'abc' ),
mk_Foo( 'b', 'def' ),
# 20 or so more
] )
@contextmanager
def deleter( path ):
control = Struct(delete=True,path=path)
try:
yield control
finally:
if control.delete:
shutil.rmtree(path)
with deleter( tempfile.mkdtemp() ) as tmp:
# do stuff with tmp.path
# most contexts don't modify the delete member
# but occasionally it's needed
if keep_tmp_dir:
tmp.delete = False
5 个回答
3
class t(dict):
def __init__(self, **kwargs):
for key, value in kwargs.items():
dict.__setitem__(self, key, value)
def __getattr__(self, key):
return dict.__getitem__(self, key)
def __setattr__(self, key, value):
raise StandardError("Cannot set attributes of tuple")
def __setitem__(self, key, value):
raise StandardError("Cannot set attributes of tuple")
def __delitem__(self, key):
raise StandardError("Cannot delete attributes of tuple")
point = t(x=10, y=500, z=-50)
print point.x # 10
print point.y # 500
print point['z'] # -50
print point # {'z': -50, 'y': 500, 'x': 10}
point.x = 100 # StandardError: cannot set attributes of tuple
point.y += 5 # StandardError: cannot set attributes of tuple
point.z = -1 # StandardError: cannot set attributes of tuple
def hypo(x, y, z):
return (x**2 + y**2 + z**2)**0.5
print hypo(point) # TypeError: unsupported operand type(s)
print hypo(**point) # 502.593274925
for k in point.items():
print k # ('y', 500)
# ('x', 10)
# ('z', -50)
for k in point.keys():
print k # x
# y
# z
for k in point.values():
print k # 500
# 10
# -50
print len(point) # 3
print dict(point) # {'y': 500, 'x': 10, 'z': -50}
这是我对这个问题的解决方案。语法很优美,不可变(至少不需要用一些麻烦的setattr()技巧),轻量级而且易于打印。虽然用这个做的事情都可以用字典(dict)来完成,
point = t(x=10, y=20, z=30)
d = point.x ** 2 + point.y ** 2 + point.z ** 2
但它和
point = (10, 20, 30)
d = point[0] ** 2 + point[1] ** 2 + point[2] ** 2
相比,整体上看起来要干净得多。
point = {'x': 10, 'y': 20, 'z': 30}
d = point['x'] ** 2 + point['y'] ** 2 + point['z'] ** 2
9
从Python 3.3开始,你可以使用types.SimpleNamespace:
>>> import types
>>> foo = types.SimpleNamespace(bar='one', baz=1)
>>> print(foo.bar)
one
>>> foo.baz += 1
>>> foo.novo = 42
这个内置类型大致相当于以下代码:
class SimpleNamespace:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
keys = sorted(self.__dict__)
items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
return "{}({})".format(type(self).__name__, ", ".join(items))
def __eq__(self, other):
return self.__dict__ == other.__dict__
更新
从Python 3.7开始,你可以使用dataclass模块:
from dataclasses import dataclass, field
@dataclass
class Struct:
bar: str = field(default='one')
baz: int = field(default=1)
你可以这样使用它:
foo = Struct( bar='one', baz=1 )
print(foo.bar)
foo.baz += 1
foo.novo = 42
默认情况下,它会包含相等性测试和一个看起来不错的表示方式:
>>> foo == Struct(bar='one', baz=2)
True
>>> foo
Struct(bar='one', baz=2)
9
这里有一个Python的做法(它只是更新实例的字典,而不是调用setattr方法)食谱52308
class Bunch(object):
def __init__(self, **kwds):
self.__dict__.update(kwds)