你可以使用数据类的修改版本,它将生成一个只包含关键字的__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 backport测试)
这需要将子类定义为
@dataclass_keyword_only
class Child(Parent):
school: str
ugly: bool = True
并且会生成__init__(self, *, name:str, age:int, ugly:bool=True, school:str)(这是有效的python)。这里唯一的警告是不允许使用位置参数初始化对象,但除此之外,它是一个完全常规的数据类,没有丑陋的hack。