Использование другого определения класса в зависимости от сигнатуры конструктора - PullRequest
1 голос
/ 21 мая 2019

Вот мой вариант использования: я хочу определить объект, который похож на кортеж, но к элементам которого я могу получить доступ по имени атрибута, например,

mytupleobj = TupObj(2012,3)
mytupleobj.year = 2012 
mytupleobj.month = 3

Питоны namedtuple являются основным кандидатом для этого, но проблема в том, что число аргументов является фиксированным. Поэтому, если нужно иметь только объекты типа кортежа, которые несут год, я должен либо создать экземпляр

mytupleobj = TupObj(2012, None)

или создайте новое определение кортежа имен, который содержит только год. Оба решения кажутся безобразными.

Есть ли способ - либо использовать namedtuple, либо какой-либо другой метод - чтобы, когда я создаю экземпляр

mytupleobj = TupObj(2012)

Я получаю объект-экземпляр, похожий на кортеж, который имеет атрибут year и только когда я использую

mytupleobj = TupObj(2012,2)

Я получил объект, похожий на кортеж, который имеет атрибуты year и month?

Ответы [ 3 ]

1 голос
/ 21 мая 2019

Вам не нужно другое определение класса.Вы просто хотите сделать эти аргументы необязательными со значением по умолчанию для атрибута, если вы не передали значение 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 модуль , или вы можете установить attrs проект .Класс данных может иметь необязательные атрибуты (по умолчанию это значение, которое вы указали во время определения):

from dataclasses import dataclass
from typing import Optional

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

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

Классы данных предоставляют значительно упрощенный синтаксис для определения небольшого класса, который поставляется с представлением, проверкой на равенство и необязательнымподдержка упорядочения, хеширования и неизменяемости.

Классы данных также полностью поддерживают наследование, что не допускается для именованных кортежей.

Класс данных не является итеративным или неизменным автоматически, но его можно сделать таким, см. это мой предыдущий ответ , где я определяю простой 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)
1 голос
/ 21 мая 2019

Если ваша цель состоит в том, чтобы иметь простую для понимания реализацию того, как это будет выглядеть с точки зрения ООП, вам просто нужно работать с условными выражениями и передавать значения по умолчанию для значений, проверяя их условное значение во время инициализации.Это может выглядеть примерно так:

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

Еще один способ очистки / реализации может состоять в том, чтобы просто сохранить значения по умолчанию как 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
1 голос
/ 21 мая 2019

Вы можете установить значения по умолчанию на вашем 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
...