Python 3.7 데이터 클래스 상속
현재 Python 3.7에서 도입된 새로운 데이터 클래스 구조를 시험하고 있습니다.나는 지금 부모 수업을 물려받으려고 애쓰고 있다.자식 클래스의 bool 매개 변수가 다른 매개 변수보다 먼저 전달되도록 현재 접근 방식에 의해 인수 순서가 엉망이 된 것 같습니다.이로 인해 타입 에러가 발생.
from dataclasses import dataclass
@dataclass
class Parent:
name: str
age: int
ugly: bool = False
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f'The Name is {self.name} and {self.name} is {self.age} year old')
@dataclass
class Child(Parent):
school: str
ugly: bool = True
jack = Parent('jack snr', 32, ugly=True)
jack_son = Child('jack jnr', 12, school = 'havard', ugly=True)
jack.print_id()
jack_son.print_id()
하면 이 코드가 됩니다.TypeError
:
TypeError: non-default argument 'school' follows default argument
이거 어떻게 고쳐야 돼요?
데이터클래스가 속성을 조합하는 방법에서는 기본 클래스의 기본 속성을 사용하여 하위 클래스의 기본(위치 속성) 없이 속성을 사용할 수 없습니다.
그 이유는 속성이 MRO의 맨 아래에서 시작하여 처음 본 순서대로 속성 목록을 작성함으로써 결합되기 때문입니다. 재정의는 원래 위치에 유지됩니다. ★★★★★★★★★★★★★★★★★.Parent
['name', 'age', 'ugly']
서, snowledge.ugly
에는 디폴트가 되어 있습니다.그 후, 「디폴트」가 표시됩니다.Child
['school']
까지 ((「」가 붙어 있다)ugly
을 참조)은, 「 」, 「 」로 것을 합니다.['name', 'age', 'ugly', 'school']
'왜냐하면'은 '왜냐하면'school
가 없기 됩니다.이것에 의해, 무효인 인수가 리스트 됩니다.__init__
.
이는 상속된 PEP-557 Dataclasses에서 문서화되어 있습니다.
가 에 되고 있는
@dataclass
즉, MRO에서 합니다).object
검색된 각 데이터 클래스에 대해 해당 기본 클래스의 필드를 필드 순서 매핑에 추가합니다.모든 기본 클래스 필드가 추가된 후 자체 필드가 순서 매핑에 추가됩니다.생성된 모든 메서드는 이 결합된 계산된 필드 매핑을 사용합니다.필드가 삽입 순서이므로 파생 클래스가 기본 클래스보다 우선합니다.
및 사양:
TypeError
기본값이 없는 필드가 기본값이 있는 필드 뒤에 있는 경우 발생합니다.이 문제는 단일 클래스에서 발생하거나 클래스 상속의 결과로 발생합니다.
이 문제를 피하기 위한 몇 가지 옵션이 있습니다.
첫 번째 옵션은 개별 기본 클래스를 사용하여 기본값이 설정된 필드를 MRO 순서에서 다음 위치로 강제 지정하는 것입니다.클래스로 에는 필드를 를 들어, 「」는 사용하지 말아 주세요.Parent
.
다음 클래스 계층이 작동합니다.
# base classes with fields; fields without defaults separate from fields with.
@dataclass
class _ParentBase:
name: str
age: int
@dataclass
class _ParentDefaultsBase:
ugly: bool = False
@dataclass
class _ChildBase(_ParentBase):
school: str
@dataclass
class _ChildDefaultsBase(_ParentDefaultsBase):
ugly: bool = True
# public classes, deriving from base-with, base-without field classes
# subclasses of public classes should put the public base class up front.
@dataclass
class Parent(_ParentDefaultsBase, _ParentBase):
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"The Name is {self.name} and {self.name} is {self.age} year old")
@dataclass
class Child(Parent, _ChildDefaultsBase, _ChildBase):
pass
필드를 기본값이 없는 필드와 기본값이 있는 필드와 신중하게 선택한 상속 순서가 있는 개별 기본 클래스로 풀하면 기본값이 없는 모든 필드를 기본값이 있는 필드보다 먼저 배치하는 MRO를 생성할 수 있습니다.역MRO(무시)object
의 경우,Child
말합니다
_ParentBase
_ChildBase
_ParentDefaultsBase
_ChildDefaultsBase
Parent
:Parent
는 새로운 필드를 설정하지 않기 때문에 필드 리스트 순서에서 '마지막'이 되는 것은 문제가 되지 않습니다. 필드를 「」 「」 「」_ParentBase
★★★★★★★★★★★★★★★★★」_ChildBase
디폴트 ( )가 _ParentDefaultsBase
★★★★★★★★★★★★★★★★★」_ChildDefaultsBase
를 참조해 주세요.
는 ★★★★★★★★★★★★★★★★★★.Parent
★★★★★★★★★★★★★★★★★」Child
계층은 가 많고, classes with with with with with with with with with with with with with with with with with with with with with classes classes classes classes classes classes.Child
은 아직 아의 of of of of of of of of of of of의 하위 입니다.Parent
:
>>> from inspect import signature
>>> signature(Parent)
<Signature (name: str, age: int, ugly: bool = False) -> None>
>>> signature(Child)
<Signature (name: str, age: int, school: str, ugly: bool = True) -> None>
>>> issubclass(Child, Parent)
True
두 클래스의 인스턴스를 만들 수 있습니다.
>>> jack = Parent('jack snr', 32, ugly=True)
>>> jack_son = Child('jack jnr', 12, school='havard', ugly=True)
>>> jack
Parent(name='jack snr', age=32, ugly=True)
>>> jack_son
Child(name='jack jnr', age=12, school='havard', ugly=True)
의 옵션은 필드만 입니다. 하여 ''를 수 . 그래도 오류가 발생하여 입력하지 않을 수 있습니다.school
「」의 으로써, 「」의 값__post_init__
:
_no_default = object()
@dataclass
class Child(Parent):
school: str = _no_default
ugly: bool = True
def __post_init__(self):
if self.school is _no_default:
raise TypeError("__init__ missing 1 required argument: 'school'")
그러나 이로 인해 필드 순서가 변경됩니다.school
ugly
:
<Signature (name: str, age: int, ugly: bool = True, school: str = <object object at 0x1101d1210>) -> None>
그리고 타입 힌트 체커에서는_no_default
★★★★★★★★★★★★★★★★★★★★★★★★★★
이 프로젝트도 사용할 수 있습니다.이 프로젝트는 이 프로젝트에 영감을 준 것입니다.dataclasses
. 전략을 를 필드 목록의 . 른 、 다 、 다 、 다 、 다 、 다 、 다 、 다 . .,, 。하위 클래스의 오버라이드된 필드를 필드 리스트의 끝으로 풀합니다.['name', 'age', 'ugly']
Parent
이 되다['name', 'age', 'school', 'ugly']
Child
; " " " " " " "attrs
MRO를 사용하다
attrs
는 타입 힌트 없이 필드의 정의를 지원하지만, 를 설정하여 지원되는 타입 힌트모드를 계속합니다.auto_attribs=True
:
import attr
@attr.s(auto_attribs=True)
class Parent:
name: str
age: int
ugly: bool = False
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"The Name is {self.name} and {self.name} is {self.age} year old")
@attr.s(auto_attribs=True)
class Child(Parent):
school: str
ugly: bool = True
init 함수에서 Atribut을 제외할 경우 부모 클래스에서 Atribut을 기본값으로 사용할 수 있습니다.초기화 시 기본값을 덮어쓸 수 있는 경우 Praveen Kulkarni의 답변으로 코드를 확장합니다.
from dataclasses import dataclass, field
@dataclass
class Parent:
name: str
age: int
ugly: bool = field(default=False, init=False)
@dataclass
class Child(Parent):
school: str
jack = Parent('jack snr', 32)
jack_son = Child('jack jnr', 12, school = 'havard')
jack_son.ugly = True
아니면 심지어
@dataclass
class Child(Parent):
school: str
ugly = True
# This does not work
# ugly: bool = True
jack_son = Child('jack jnr', 12, school = 'havard')
assert jack_son.ugly
Python 3.10을 사용하면 데이터클래스로 네이티브하게 실행할 수 있습니다.
3은 Dataclass 3.10†을 추가하였습니다.kw_only
속성(아트리뷰트와 유사).이를 통해 어떤 필드가 키워드_only인지 지정할 수 있으므로 init 종료 시 설정되며 상속 문제가 발생하지 않습니다.
Eric Smith의 블로그 투고에서 직접 인용하면, 이 기능을 요구하는 이유는 다음과 같습니다.
- 데이터 클래스에 필드가 많을 경우 위치별로 필드를 지정하면 읽을 수 없게 될 수 있습니다.또한 하위 호환성을 위해 모든 새 필드를 데이터 클래스 끝에 추가해야 합니다.이것이 항상 바람직한 것은 아닙니다.
- 데이터 클래스가 다른 데이터 클래스에서 상속되고 기본 클래스에 기본값 필드가 있는 경우 파생 클래스의 모든 필드에도 기본값이 있어야 합니다.
이 새로운 인수를 사용하는 가장 간단한 방법은 다음과 같습니다만, 부모 클래스의 기본값을 사용하여 상속을 사용하는 방법은 여러 가지가 있습니다.
from dataclasses import dataclass
@dataclass(kw_only=True)
class Parent:
name: str
age: int
ugly: bool = False
@dataclass(kw_only=True)
class Child(Parent):
school: str
ch = Child(name="Kevin", age=17, school="42")
print(ch.ugly)
kw_only에 대한 자세한 설명은 위의 블로그 포스트를 참조하십시오.
건배!
PS: 꽤 새로운 제품이기 때문에, IDE가 에러를 일으킬 가능성이 있습니다만, 실행시에 동작하는 것에 주의해 주세요.
비단뱀을 하여 이 합니다.dataclasses
보일러 플레이트 코드도 별로 없고
ugly_init: dataclasses.InitVar[bool]
초기화를 지원하는 의사 필드 역할을 하며 인스턴스가 생성되면 손실됩니다.하는 동안에ugly: bool = field(init=False)
는 인스턴스 멤버는 .__init__
할 수도 있지만, 다른 할 수도 .__post_init__
method(자세한 내용은 여기를 참조해 주세요).
from dataclasses import dataclass, field
@dataclass
class Parent:
name: str
age: int
ugly: bool = field(init=False)
ugly_init: dataclasses.InitVar[bool]
def __post_init__(self, ugly_init: bool):
self.ugly = ugly_init
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f'The Name is {self.name} and {self.name} is {self.age} year old')
@dataclass
class Child(Parent):
school: str
jack = Parent('jack snr', 32, ugly_init=True)
jack_son = Child('jack jnr', 12, school='havard', ugly_init=True)
jack.print_id()
jack_son.print_id()
는, 「」를 참조해 주세요.ugly_init
이며, 할 수 메서드에는 '클래스하다'가 포함됩니다.다음과 같은 클래스 메서드를 부모 상에서 정의할 수 있습니다.ugly_init
옵션 파라미터로서 다음과 같이 입력합니다.
from dataclasses import dataclass, field, InitVar
@dataclass
class Parent:
name: str
age: int
ugly: bool = field(init=False)
ugly_init: InitVar[bool]
def __post_init__(self, ugly_init: bool):
self.ugly = ugly_init
@classmethod
def create(cls, ugly_init=True, **kwargs):
return cls(ugly_init=ugly_init, **kwargs)
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f'The Name is {self.name} and {self.name} is {self.age} year old')
@dataclass
class Child(Parent):
school: str
jack = Parent.create(name='jack snr', age=32, ugly_init=False)
jack_son = Child.create(name='jack jnr', age=12, school='harvard')
jack.print_id()
jack_son.print_id()
, 그럼 에는 '어울리지 않다'를 사용하시면 .create
method는 class method의 으로 부모는 class method의 디폴트값입니다.ugly_init
이 접근방식이 기능하려면 명명된 파라미터를 사용해야 합니다.
기본값이 없는 인수가 기본값이 있는 인수 뒤에 추가되었기 때문에 이 오류가 나타납니다.상속된 필드를 데이터 클래스에 삽입하는 순서는 메서드 해결 순서와 반대입니다. 즉, 이 순서는Parent
필드가 우선입니다.자녀가 나중에 덮어쓰더라도 말이죠.
@dataclass class Base: x: Any = 15.0 y: int = 0 @dataclass class C(Base): z: int = 10 x: int = 15
정리합니다.
x, y, z
of of of의x
int
class에서 「」를 참조해 주세요).C
.
불행하게도, 나는 이것을 피할 방법이 없다고 생각한다.부모 클래스에 default 인수가 있는 경우 어떤 자식 클래스에도 non-default 인수가 있을 수 없습니다.
Martijn Pieters 솔루션을 기반으로 다음 작업을 수행했습니다.
1) post_init를 구현한 혼합 작성
from dataclasses import dataclass
no_default = object()
@dataclass
class NoDefaultAttributesPostInitMixin:
def __post_init__(self):
for key, value in self.__dict__.items():
if value is no_default:
raise TypeError(
f"__init__ missing 1 required argument: '{key}'"
)
2) 다음으로 상속 문제가 있는 클래스에서 다음을 수행합니다.
from src.utils import no_default, NoDefaultAttributesChild
@dataclass
class MyDataclass(DataclassWithDefaults, NoDefaultAttributesPostInitMixin):
attr1: str = no_default
편집:
잠시 후 mypy에서도 이 솔루션에 문제가 발견되면 다음 코드가 문제를 해결합니다.
from dataclasses import dataclass
from typing import TypeVar, Generic, Union
T = TypeVar("T")
class NoDefault(Generic[T]):
...
NoDefaultVar = Union[NoDefault[T], T]
no_default: NoDefault = NoDefault()
@dataclass
class NoDefaultAttributesPostInitMixin:
def __post_init__(self):
for key, value in self.__dict__.items():
if value is NoDefault:
raise TypeError(f"__init__ missing 1 required argument: '{key}'")
@dataclass
class Parent(NoDefaultAttributesPostInitMixin):
a: str = ""
@dataclass
class Child(Foo):
b: NoDefaultVar[str] = no_default
가능한 회피책으로는 monkey-patching을 사용하여 부모 필드를 추가하는 방법이 있습니다.
import dataclasses as dc
def add_args(parent):
def decorator(orig):
"Append parent's fields AFTER orig's fields"
# Aggregate fields
ff = [(f.name, f.type, f) for f in dc.fields(dc.dataclass(orig))]
ff += [(f.name, f.type, f) for f in dc.fields(dc.dataclass(parent))]
new = dc.make_dataclass(orig.__name__, ff)
new.__doc__ = orig.__doc__
return new
return decorator
class Animal:
age: int = 0
@add_args(Animal)
class Dog:
name: str
noise: str = "Woof!"
@add_args(Animal)
class Bird:
name: str
can_fly: bool = True
Dog("Dusty", 2) # --> Dog(name='Dusty', noise=2, age=0)
b = Bird("Donald", False, 40) # --> Bird(name='Donald', can_fly=False, age=40)
기본값이 아닌 필드 앞에 추가할 수도 있습니다.if f.default is dc.MISSING
하지만 이것은 아마도 너무 더러울 것이다.
원숭이 패칭에는 상속 기능이 없지만 모든 유사 자식 클래스에 메서드를 추가하는 데 사용할 수 있습니다.
제어를 는 을 사용하여 합니다.dc.field(compare=False, repr=True, ...)
된 버전의 수 있습니다.이 경우 이 키워드만 됩니다.__init__
★★★★
import dataclasses
def _init_fn(fields, frozen, has_post_init, self_name):
# fields contains both real fields and InitVar pseudo-fields.
globals = {'MISSING': dataclasses.MISSING,
'_HAS_DEFAULT_FACTORY': dataclasses._HAS_DEFAULT_FACTORY}
body_lines = []
for f in fields:
line = dataclasses._field_init(f, frozen, globals, self_name)
# line is None means that this field doesn't require
# initialization (it's a pseudo-field). Just skip it.
if line:
body_lines.append(line)
# Does this class have a post-init function?
if has_post_init:
params_str = ','.join(f.name for f in fields
if f._field_type is dataclasses._FIELD_INITVAR)
body_lines.append(f'{self_name}.{dataclasses._POST_INIT_NAME}({params_str})')
# If no body lines, use 'pass'.
if not body_lines:
body_lines = ['pass']
locals = {f'_type_{f.name}': f.type for f in fields}
return dataclasses._create_fn('__init__',
[self_name, '*'] + [dataclasses._init_param(f) for f in fields if f.init],
body_lines,
locals=locals,
globals=globals,
return_type=None)
def add_init(cls, frozen):
fields = getattr(cls, dataclasses._FIELDS)
# Does this class have a post-init function?
has_post_init = hasattr(cls, dataclasses._POST_INIT_NAME)
# Include InitVars and regular fields (so, not ClassVars).
flds = [f for f in fields.values()
if f._field_type in (dataclasses._FIELD, dataclasses._FIELD_INITVAR)]
dataclasses._set_new_attribute(cls, '__init__',
_init_fn(flds,
frozen,
has_post_init,
# The name to use for the "self"
# param in __init__. Use "self"
# if possible.
'__dataclass_self__' if 'self' in fields
else 'self',
))
return cls
# a dataclass with a constructor that only takes keyword arguments
def dataclass_keyword_only(_cls=None, *, repr=True, eq=True, order=False,
unsafe_hash=False, frozen=False):
def wrap(cls):
cls = dataclasses.dataclass(
cls, init=False, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen)
return add_init(cls, frozen)
# See if we're being called as @dataclass or @dataclass().
if _cls is None:
# We're called with parens.
return wrap
# We're called as @dataclass without parens.
return wrap(_cls)
(Python 3.6 백포트에서 테스트된 Gist로도 게시됨)
이를 위해서는 자클래스를 다음과 같이 정의해야 합니다.
@dataclass_keyword_only
class Child(Parent):
school: str
ugly: bool = True
그리고 생성될 것이다__init__(self, *, name:str, age:int, ugly:bool=True, school:str)
비단뱀해야 할 점은 인수로 할 수 없다는 입니다. 않으면 인 위치 인수가 .dataclass
못생긴 해크도 없이.
신속하고 지저분한 솔루션:
from typing import Optional
@dataclass
class Child(Parent):
school: Optional[str] = None
ugly: bool = True
def __post_init__(self):
assert self.school is not None
그런 다음 다시 돌아가서 언어가 확장되면(바람직하게) 다시 분석합니다.
데이터클래스가 필드의 순서를 변경할 수 있는 데코레이터 파라미터를 얻을 수 있다는 것을 발견하고 이 질문으로 돌아왔습니다.이것은 확실히 유망한 발전이지만, 이 기능의 진전은 다소 지연되고 있는 것 같습니다.
현재 데이터 클래스(dataclassy)를 사용하여 이러한 행동을 할 수 있으며, 이와 같은 불만을 극복하는 데이터 클래스(dataclassy)를 재실장할 수 있습니다.사용.from dataclassy
from dataclasses
원래 예에서는 오류 없이 실행됨을 의미합니다.
inspect를 사용하여 시그니처 인쇄Child
있는지를그 결과, 「이러다.」가 됩니다.(name: str, age: int, school: str, ugly: bool = True)
. 필드의 순서는 항상 기본값이 있는 필드가 이니셜라이저 파라미터에 없는 필드 뒤에 오도록 조정됩니다.두 목록(기본값이 없는 필드 및 필드가 있는 필드)은 여전히 정의 순서대로 정렬됩니다.
이 문제에 직면하게 된 것이 데이터클래스의 대체품을 쓰게 된 요인 중 하나였습니다.여기서 설명하는 회피책은 도움이 되지만 가독성 우위 데이터클래스의 순진한 접근법(필드 순서를 3가지로 예측할 수 있는)을 완전히 부정할 정도로 코드를 왜곡할 필요가 있다.
Python 상속을 사용하여 데이터클래스를 만들 때 기본값을 가진 모든 필드가 기본값 없는 모든 필드 뒤에 표시되도록 보장할 수 없습니다.
간단한 해결책은 여러 상속을 사용하여 "머지된" 데이터 클래스를 구성하는 것을 피하는 것입니다.대신 부모 데이터클래스 필드에서 필터링 및 정렬하는 것만으로 병합된 데이터클래스를 구축할 수 있습니다.
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★merge_dataclasses()
★★★★
import dataclasses
import functools
from typing import Iterable, Type
def merge_dataclasses(
cls_name: str,
*,
merge_from: Iterable[Type],
**kwargs,
):
"""
Construct a dataclass by merging the fields
from an arbitrary number of dataclasses.
Args:
cls_name: The name of the constructed dataclass.
merge_from: An iterable of dataclasses
whose fields should be merged.
**kwargs: Keyword arguments are passed to
:py:func:`dataclasses.make_dataclass`.
Returns:
Returns a new dataclass
"""
# Merge the fields from the dataclasses,
# with field names from later dataclasses overwriting
# any conflicting predecessor field names.
each_base_fields = [d.__dataclass_fields__ for d in merge_from]
merged_fields = functools.reduce(
lambda x, y: {**x, **y}, each_base_fields
)
# We have to reorder all of the fields from all of the dataclasses
# so that *all* of the fields without defaults appear
# in the merged dataclass *before* all of the fields with defaults.
fields_without_defaults = [
(f.name, f.type, f)
for f in merged_fields.values()
if isinstance(f.default, dataclasses._MISSING_TYPE)
]
fields_with_defaults = [
(f.name, f.type, f)
for f in merged_fields.values()
if not isinstance(f.default, dataclasses._MISSING_TYPE)
]
fields = [*fields_without_defaults, *fields_with_defaults]
return dataclasses.make_dataclass(
cls_name=cls_name,
fields=fields,
**kwargs,
)
그런 다음 다음과 같이 데이터클래스를 병합할 수 있습니다. 해서 합칠 수 점 유의하세요.A
★★★★★★★★★★★★★★★★★」B
필드 " " " 입니다.b
★★★★★★★★★★★★★★★★★」d
병합된 데이터 클래스 끝으로 이동합니다.
@dataclasses.dataclass
class A:
a: int
b: int = 0
@dataclasses.dataclass
class B:
c: int
d: int = 0
C = merge_dataclasses(
"C",
merge_from=[A, B],
)
# Note that
print(C(a=1, d=1).__dict__)
# {'a': 1, 'd': 1, 'b': 0, 'c': 0}
이 은 '먹다'는 것이다.C
에서 실제로 상속되지 않는다A
★★★★★★★★★★★★★★★★★」B
아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아,isinstance()
또는 C의 혈통을 확인하기 위한 다른 유형의 어설션.
Python 3.10+를 사용하는 경우 이 답변과 python 문서에서 설명한 대로 데이터 클래스에 키워드 전용 인수를 사용할 수 있습니다.
3을 사용하고 는,< Python 3.10 을 사용할 수 .dataclasses.field
a default_factory
집니 since 。은 음음음음으로 선언되기 field()
는 기본값이 설정되어 있는 것처럼 처리되지만 사용자가 해당 필드의 값을 지정하지 않고 인스턴스를 작성하려고 하면 공장 출고가 사용되므로 오류가 발생합니다.
이 기술은 모든 인수를 위치별로 제공할 수 있기 때문에 키워드에만 해당되지 않습니다.단, 이것으로 문제가 해결되어 다양한 데이터 클래스 던더 메서드를 조작하는 것보다 간단합니다.
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional, TypeVar
T = TypeVar("T")
def required() -> T:
f: T
def factory() -> T:
# mypy treats a Field as a T, even though it has attributes like .name, .default, etc
field_name = f.name # type: ignore[attr-defined]
raise ValueError(f"field '{field_name}' required")
f = field(default_factory=factory)
return f
@dataclass
class Event:
id: str
created_at: datetime
updated_at: Optional[datetime] = None
@dataclass
class NamedEvent(Event):
name: str = required()
event = NamedEvent(name="Some Event", id="ab13c1a", created_at=datetime.now())
print("created event:", event)
event2 = NamedEvent("ab13c1a", datetime.now(), name="Some Other Event")
print("created event:", event2)
event3 = NamedEvent("ab13c1a", datetime.now())
출력:
created event: NamedEvent(id='ab13c1a', created_at=datetime.datetime(2022, 7, 23, 19, 22, 17, 944550), updated_at=None, name='Some Event')
created event: NamedEvent(id='ab13c1a', created_at=datetime.datetime(2022, 7, 23, 19, 22, 17, 944588), updated_at=None, name='Some Other Event')
Traceback (most recent call last):
File ".../gist.py", line 39, in <module>
event3 = NamedEvent("ab13c1a", datetime.now())
File "<string>", line 6, in __init__
File ".../gist.py", line 14, in factory
raise ValueError(f"field '{field_name}' required")
ValueError: field 'name' required
이 코드는 이 github gist에서도 찾을 수 있습니다.
특성을 사용하는 Martijn Pieters 솔루션을 보완: 기본 특성 복제 없이 다음과 같은 방법으로 상속을 생성할 수 있습니다.
import attr
@attr.s(auto_attribs=True)
class Parent:
name: str
age: int
ugly: bool = attr.ib(default=False, kw_only=True)
@attr.s(auto_attribs=True)
class Child(Parent):
school: str
ugly: bool = True
the 자세한 것은,kw_only
파라미터는 여기서 찾을 수 있습니다.
의 를 해 볼까요?ugly
디폴트 방식 대신 이렇게 입력하시겠습니까?
ugly: bool = field(metadata=dict(required=False, missing=False))
실험적이지만 흥미로운 해결책은 메타클래스를 사용하는 것입니다.에서는 Python을 하지 않고 과 함께 Python 할 수 .dataclass
이치노또한 positional 인수(비기본 필드)의 순서에 대해 불평하지 않고 부모 기본 클래스의 필드를 상속할 수 있습니다.
from collections import OrderedDict
import typing as ty
import dataclasses
from itertools import takewhile
class DataClassTerm:
def __new__(cls, *args, **kwargs):
return super().__new__(cls)
class DataClassMeta(type):
def __new__(cls, clsname, bases, clsdict):
fields = {}
# Get list of base classes including the class to be produced(initialized without its original base classes as those have already become dataclasses)
bases_and_self = [dataclasses.dataclass(super().__new__(cls, clsname, (DataClassTerm,), clsdict))] + list(bases)
# Whatever is a subclass of DataClassTerm will become a DataClassTerm.
# Following block will iterate and create individual dataclasses and collect their fields
for base in bases_and_self[::-1]: # Ensure that last fields in last base is prioritized
if issubclass(base, DataClassTerm):
to_dc_bases = list(takewhile(lambda c: c is not DataClassTerm, base.__mro__))
for dc_base in to_dc_bases[::-1]: # Ensure that last fields in last base in MRO is prioritized(same as in dataclasses)
if dataclasses.is_dataclass(dc_base):
valid_dc = dc_base
else:
valid_dc = dataclasses.dataclass(dc_base)
for field in dataclasses.fields(valid_dc):
fields[field.name] = (field.name, field.type, field)
# Following block will reorder the fields so that fields without default values are first in order
reordered_fields = OrderedDict()
for n, t, f in fields.values():
if f.default is dataclasses.MISSING and f.default_factory is dataclasses.MISSING:
reordered_fields[n] = (n, t, f)
for n, t, f in fields.values():
if n not in reordered_fields.keys():
reordered_fields[n] = (n, t, f)
# Create a new dataclass using `dataclasses.make_dataclass`, which ultimately calls type.__new__, which is the same as super().__new__ in our case
fields = list(reordered_fields.values())
full_dc = dataclasses.make_dataclass(cls_name=clsname, fields=fields, init=True, bases=(DataClassTerm,))
# Discard the created dataclass class and create new one using super but preserve the dataclass specific namespace.
return super().__new__(cls, clsname, bases, {**full_dc.__dict__,**clsdict})
class DataClassCustom(DataClassTerm, metaclass=DataClassMeta):
def __new__(cls, *args, **kwargs):
if len(args)>0:
raise RuntimeError("Do not use positional arguments for initialization.")
return super().__new__(cls, *args, **kwargs)
이제 부모 데이터 클래스와 샘플 혼합 클래스를 사용하여 샘플 데이터 클래스를 만듭니다.
class DataClassCustomA(DataClassCustom):
field_A_1: int = dataclasses.field()
field_A_2: ty.AnyStr = dataclasses.field(default=None)
class SomeOtherClass:
def methodA(self):
print('print from SomeOtherClass().methodA')
class DataClassCustomB(DataClassCustomA,SomeOtherClass):
field_B_1: int = dataclasses.field()
field_B_2: ty.Dict = dataclasses.field(default_factory=dict)
그 결과는
result_b = DataClassCustomB(field_A_1=1, field_B_1=2)
result_b
# DataClassCustomB(field_A_1=1, field_B_1=2, field_A_2=None, field_B_2={})
result_b.methodA()
# print from SomeOtherClass().methodA
에 대해 동일한 작업을 수행하려고 합니다.@dataclass
클래스에서 입니다.TypeError(f'non-default argument <field-name) follows default argument')
위의 솔루션에서는 필드의 순서가 먼저 변경되므로 이 문제가 발생하지 않습니다.에, ,, 드, 필, 음, 음의 방지가 .*args
, 사용방법)DataClassCustom.__new__
원래 주문이 유효하지 않기 때문에 필수입니다.
Python > = 3.에서는 Python > = 3.10입니다.kw_only
의 신뢰성을 크게 할 수 .데이터클래스를 할 필요가 . 위의 예는 여전히 데이터클래스를 사용할 필요가 없는 상속 가능한 데이터클래스를 만드는 방법으로 사용될 수 있습니다.@dataclass
데코레이터
언급URL : https://stackoverflow.com/questions/51575931/class-inheritance-in-python-3-7-dataclasses
'programing' 카테고리의 다른 글
numpy.random.seed(0)의 역할은 무엇입니까? (0) | 2022.10.06 |
---|---|
Vue router-link 완전 액티브클래스 (0) | 2022.10.06 |
MySQL: 자동 커밋 플래그가 켜져 있지만 트랜잭션이 롤백될 수 있습니다. (0) | 2022.10.06 |
Java 로깅 출력을 한 줄에 표시하려면 어떻게 해야 합니까? (0) | 2022.10.06 |
PHP exec() vs system() vs 패스스루() (0) | 2022.09.29 |