[英]Using a different class definition depending on the constructor signature
Here's my use case: I want to define an object that is like a tuple, but whose elements I can access by attribute name, eg 这是我的用例:我想定义一个类似元组的对象,但我可以通过属性名访问其元素,例如
mytupleobj = TupObj(2012,3)
mytupleobj.year = 2012
mytupleobj.month = 3
Pythons namedtuple
are the prime candidate for this, but the problem is that the number of arguments is fixed. Pythons namedtuple
是这个的主要候选者,但问题是参数的数量是固定的。 So if need to only have tuple-like objects that carry the year, I either have to instaniate 因此,如果只需要具有年份的元组类对象,我要么必须实现
mytupleobj = TupObj(2012, None)
or create a new definition of a name-tuple that only carries the year. 或者创建一个仅包含年份的名称元组的新定义。 Both solution seem ugly. 两种解决方案看起来都很丑
Is there a way -either using namedtuple
or some other technique- so that when I instantiate 有没有办法 - 使用namedtuple
或其他一些技术 - 以便在我实例化时
mytupleobj = TupObj(2012)
I get a tuple-like object instantiable that has attribute year
only and when I use 我得到一个类似于元组的对象,它只有属性year
和我使用时
mytupleobj = TupObj(2012,2)
I get a tuple-like object that has year
and month
attributes? 我得到一个具有year
和month
属性的类似元组的对象?
You can set defaults on your namedtuple
您可以在namedtuple
上设置默认值
Python 2.7 Solution: Python 2.7解决方案:
from collections import namedtuple
tupobj = namedtuple('tupobj', 'year month')
tupobj.__new__.__defaults__ = (None,) * len(tupobj._fields)
t1 = tupobj(2012)
print(t1)
# >> tupobj(year=2012, month=None)
print(t1.year)
# >> 2012
t2 = tupobj(year=2012)
print(t2)
# >> tupobj(year=2012, month=None)
print(t2.year)
# >> 2012
t3 = tupobj(month=1)
print(t3)
# >> tupobj(year=None, month=1)
print(t3.month)
# >> 1
t4 = tupobj(2012, 1)
print(t4)
# >> tupobj(year=2012, month=1)
print(t4.year)
# >> 2012
print(t4.month)
# >> 1
Python 3.7 Solution: Python 3.7解决方案:
from collections import namedtuple
tupobj = namedtuple('tupobj', 'year month', defaults=(None,None))
t1 = tupobj(2012)
print(t1)
# >> tupobj(year=2012, month=None)
print(t1.year)
# >> 2012
t2 = tupobj(year=2012)
print(t2)
# >> tupobj(year=2012, month=None)
print(t2.year)
# >> 2012
t3 = tupobj(month=1)
print(t3)
# >> tupobj(year=None, month=1)
print(t3.month)
# >> 1
t4 = tupobj(2012, 1)
print(t4)
# >> tupobj(year=2012, month=1)
print(t4.year)
# >> 2012
print(t4.month)
# >> 1
You do not want a different class definition. 您不需要不同的类定义。 You merely want to make those arguments optional with a default value for the attribute if you didn't pass in a month
value. 如果没有传入month
值,您只想使用属性的默认值使这些参数可选 。 The namedtuple()
factory function doesn't support that use-case. namedtuple()
工厂函数不支持该用例。
But that's no the only way to create a named tuple. 但这不是创建命名元组的唯一方法。 You can also subclass typing.NamedTuple
: 您还可以子类化typing.NamedTuple
:
from typing import NamedTuple, Optional
class VagueTimePeriod(NamedTuple):
year: int
month: Optional[int] = None
That's a class definition for a named tuple where month
is optional, if you don't specify a month it is left to the default value: 这是一个命名元组的类定义,其中month
是可选的,如果你没有指定一个月它将保留为默认值:
>>> VagueTimePeriod(2012)
VagueTimePeriod(year=2012, month=None)
>>> VagueTimePeriod(2012, 3)
VagueTimePeriod(year=2012, month=3)
However, I suspect that what you really want is a dataclass . 但是,我怀疑你真正想要的是一个数据类 。 A simple class that mostly just holds some data. 一个简单的类,主要是持有一些数据。
Python 3.7 has the new dataclasses
module , or you can install the attrs
project . Python 3.7有新的dataclasses
模块 ,或者你可以安装attrs
项目 。 A dataclass can have optional attributes (defaulting to a value you state at definition time): 数据类可以具有可选属性(默认为您在定义时声明的值):
from dataclasses import dataclass
from typing import Optional
@dataclass
class VagueTimePeriod:
year: int
month: Optional[int] = None
vtp1 = VagueTimePeriod(2012)
vtp2 = VagueTimePeriod(2012, 3)
Dataclasses give you greatly simplified syntax to define a small class, which comes with a representation, equality testing, and optional ordering support, hashing, and immutability. 数据类为您提供了极大简化的语法来定义一个小类,它带有表示,相等测试和可选的排序支持,散列和不变性。
Dataclasses also fully support inheritance, which named tuples don't. 数据类也完全支持继承,而元组则没有。
A dataclass is not automatically iterable or immutable, but can be made so, see this previous answer of mine , where I define a simple DataclassSequence
base class that adds sequence behaviour. 数据类不能自动迭代或不可变,但可以这样做,请参阅我之前的答案 ,其中我定义了一个简单的DataclassSequence
基类,它添加了序列行为。
A quick demo: 快速演示:
>>> @dataclass(frozen=True)
... class VagueTimePeriod:
... year: int
... month: Optional[int] = None
...
>>> VagueTimePeriod(2012)
VagueTimePeriod(year=2012, month=None)
VagueTimePeriod(2012, 3)
VagueTimePeriod(year=2012, month=3)
If your goal is to have an easy to understand implementation of how this would look from an OOP perspective, you just need to work with conditionals and pass default arguments for values, checking for their conditional value during the init. 如果你的目标是从OOP角度看一个易于理解的实现,你只需要处理条件并传递值的默认参数,在init期间检查它们的条件值。 It may look something like this: 它可能看起来像这样:
class mydatetimeclass():
def __init__(self, year, month=None, day=None):
self.year = year
if month is not None:
self.month = month
if day is not None:
self.day = day
obj1 = mydatetimeclass(2016)
obj1.year #2016
obj2 = mydatetimeclass(2017, 5)
obj2.year #2017
obj2.month #5
Another cleaner to implement/maintain approach can be to just have the default values saved as None, so that you do not have to worry about which attributes actually exist in each object. 实现/维护方法的另一个清理方法是将默认值保存为None,这样您就不必担心每个对象中实际存在哪些属性。
class mydatetimeclass():
def __init__(self, year, month=None, day=None):
self.year = year
self.month = month #sets to None by default
self.day = day
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.