根据构造函数签名使用不同的 class 定义
Using a different class definition depending on the constructor signature
这是我的用例:我想定义一个类似于元组的对象,但我可以通过属性名称访问其元素,例如
mytupleobj = TupObj(2012,3)
mytupleobj.year = 2012
mytupleobj.month = 3
Python namedtuple
是这方面的主要候选者,但问题是参数的数量是固定的。所以如果只需要带有年份的类似元组的对象,我要么必须实例化
mytupleobj = TupObj(2012, None)
或者创建一个只包含年份的名称元组的新定义。这两种解决方案看起来都很丑。
有没有办法 - 使用 namedtuple
或其他一些技术 - 以便在我实例化时
mytupleobj = TupObj(2012)
我得到一个类似元组的可实例化对象,它只有 year
属性,当我使用
时
mytupleobj = TupObj(2012,2)
我得到一个具有 year
和 month
属性的类似元组的对象?
您可以在 namedtuple
上设置默认值
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 解法:
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
您不需要不同的 class 定义。如果您没有传入 month
值,您只想使这些参数 可选 具有属性的默认值。 namedtuple()
工厂函数不支持 use-case.
但这不是创建命名元组的唯一方法。你也可以subclass typing.NamedTuple
:
from typing import NamedTuple, Optional
class VagueTimePeriod(NamedTuple):
year: int
month: Optional[int] = None
这是命名元组的 class 定义,其中 month
是可选的,如果您不指定月份,则保留默认值:
>>> VagueTimePeriod(2012)
VagueTimePeriod(year=2012, month=None)
>>> VagueTimePeriod(2012, 3)
VagueTimePeriod(year=2012, month=3)
不过,我怀疑你真正想要的是一个数据class。一个简单的 class,大部分只包含一些数据。
Python 3.7 有新的 dataclasses
module, or you can install the attrs
project。数据class 可以具有可选属性(默认为您在定义时声明的值):
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 大大简化了定义小 class 的语法,它带有表示、相等性测试和可选的排序支持、散列和不变性。
Dataclasses 也完全支持继承,命名元组不支持继承。
A dataclass 不是自动可迭代或不可变的,但可以这样做,参见 ,我在其中定义了一个简单的 DataclassSequence
base class 添加序列行为。
快速演示:
>>> @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)
如果您的目标是从 OOP 的角度实现一个易于理解的实现,您只需要使用条件并传递值的默认参数,在初始化期间检查它们的条件值。它可能看起来像这样:
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
另一种比 implement/maintain 更简洁的方法是将默认值保存为 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
这是我的用例:我想定义一个类似于元组的对象,但我可以通过属性名称访问其元素,例如
mytupleobj = TupObj(2012,3)
mytupleobj.year = 2012
mytupleobj.month = 3
Python namedtuple
是这方面的主要候选者,但问题是参数的数量是固定的。所以如果只需要带有年份的类似元组的对象,我要么必须实例化
mytupleobj = TupObj(2012, None)
或者创建一个只包含年份的名称元组的新定义。这两种解决方案看起来都很丑。
有没有办法 - 使用 namedtuple
或其他一些技术 - 以便在我实例化时
mytupleobj = TupObj(2012)
我得到一个类似元组的可实例化对象,它只有 year
属性,当我使用
mytupleobj = TupObj(2012,2)
我得到一个具有 year
和 month
属性的类似元组的对象?
您可以在 namedtuple
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 解法:
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
您不需要不同的 class 定义。如果您没有传入 month
值,您只想使这些参数 可选 具有属性的默认值。 namedtuple()
工厂函数不支持 use-case.
但这不是创建命名元组的唯一方法。你也可以subclass typing.NamedTuple
:
from typing import NamedTuple, Optional
class VagueTimePeriod(NamedTuple):
year: int
month: Optional[int] = None
这是命名元组的 class 定义,其中 month
是可选的,如果您不指定月份,则保留默认值:
>>> VagueTimePeriod(2012)
VagueTimePeriod(year=2012, month=None)
>>> VagueTimePeriod(2012, 3)
VagueTimePeriod(year=2012, month=3)
不过,我怀疑你真正想要的是一个数据class。一个简单的 class,大部分只包含一些数据。
Python 3.7 有新的 dataclasses
module, or you can install the attrs
project。数据class 可以具有可选属性(默认为您在定义时声明的值):
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 大大简化了定义小 class 的语法,它带有表示、相等性测试和可选的排序支持、散列和不变性。
Dataclasses 也完全支持继承,命名元组不支持继承。
A dataclass 不是自动可迭代或不可变的,但可以这样做,参见 DataclassSequence
base class 添加序列行为。
快速演示:
>>> @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)
如果您的目标是从 OOP 的角度实现一个易于理解的实现,您只需要使用条件并传递值的默认参数,在初始化期间检查它们的条件值。它可能看起来像这样:
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
另一种比 implement/maintain 更简洁的方法是将默认值保存为 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