简体   繁体   English

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

[英]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? 我得到一个具有yearmonth属性的类似元组的对象?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM