根据构造函数签名使用不同的类定义

时间:2019-05-21 09:51:39

标签: python oop attributes tuples namedtuple

这是我的用例:我想定义一个像元组的对象,但是我可以通过属性名称访问其元素,例如

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)

我得到一个具有yearmonth属性的类似元组的对象吗?

3 个答案:

答案 0 :(得分:1)

您可以在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

答案 1 :(得分:1)

您不需要其他的类定义。如果您未传递month值,则只想使用属性的默认值使这些参数可选即可。 namedtuple()工厂功能不支持该用例。

但这不是创建命名元组的唯一方法。您还可以继承typing.NamedTuple的子类:

from typing import NamedTuple, Optional

class VagueTimePeriod(NamedTuple): 
    year: int
    month: Optional[int] = None

这是命名元组的类定义,其中month是可选的,如果您不指定月份,则将其保留为默认值:

>>> VagueTimePeriod(2012)
VagueTimePeriod(year=2012, month=None)
>>> VagueTimePeriod(2012, 3)
VagueTimePeriod(year=2012, month=3)

但是,我怀疑您真正想要的是数据类。一个简单的类,大部分只保存一些数据。

Python 3.7具有新的dataclasses module,或者您可以安装attrs project。数据类可以具有可选属性(默认为您在定义时指定的值):

from dataclasses import dataclass
from typing import Optional

@dataclass
class VagueTimePeriod:
    year: int
    month: Optional[int] = None

vtp1 = VagueTimePeriod(2012)
vtp2 = VagueTimePeriod(2012, 3)

数据类为您提供了一种简化的语法,用于定义一个小类,该小类具有表示法,相等性测试以及可选的排序支持,哈希和不变性。

数据类也完全支持继承,而元组则不支持。

数据类不是自动可迭代或不可变的,但可以做到这一点,请参见this previous answer of mine,其中我定义了一个简单的DataclassSequence基类,该基类增加了序列行为。

快速演示:

>>> @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)

答案 2 :(得分:1)

如果您的目标是要有一个易于理解的实现,以实现从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

另一种更易于实施/维护的方法是将默认值保存为“无”,这样您就不必担心每个对象中实际存在哪些属性。

class mydatetimeclass():
    def __init__(self, year, month=None, day=None):
        self.year = year
        self.month = month #sets to None by default
        self.day = day