如何根据输入参数以不同的方式初始化 NamedTuple child class?
How to initialize a NamedTuple child class different ways based on input arguments?
我正在构建一个可以接受不同初始化方式的 typing.NamedTuple class(see typing.NamedTuple docs here, or the older collections.namedtuples docs 它继承自)。
为什么在这种情况下使用 NamedTuple?我希望它是不可变的和可自动散列的,这样它就可以成为字典键,而且我不必编写散列函数。
我知道我需要使用 __new__
而不是 __init__
,因为 NamedTuples 是不可变的(例如,. I've searched and there are some tidbits out there (e.g. answers to this question on setting up a custom hash for a namedtuple),但我无法让一切正常,我收到有关无法覆盖 __new__
.
的错误
这是我当前的代码:
from typing import NamedTuple
class TicTacToe(NamedTuple):
"""A tic-tac-toe board, each character is ' ', 'x', 'o'"""
row1: str = ' '
row2: str = ' '
row3: str = ' '
def __new__(cls, *args, **kwargs):
print(f'Enter __new__ with {cls}, {args}, {kwargs}')
if len(args) == 1 and args[0] == 0:
new_args = (' ', ' ', ' ')
else:
new_args = args
self = super().__new__(cls, *new_args, *kwargs)
return self
if __name__ == '__main__':
a = TicTacToe(('xo ', 'x x', 'o o'))
print(a)
b = TicTacToe(0)
print(b)
但我收到以下错误:
Traceback (most recent call last):
File "c:/Code/lightcc/OpenPegs/test_namedtuple.py", line 4, in <module>
class TicTacToe(NamedTuple):
File "C:\Dev\Python37\lib\typing.py", line 1384,
in __new__
raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
AttributeError: Cannot overwrite NamedTuple attribute __new__
我是否无法为继承自 NamedTuple 的子 class 创建单独的 __new__
函数?从消息中可以看出,它正试图直接覆盖 NamedTuple 的 __new__
,而不是 TicTacToe class.
这是怎么回事?
您可以通过定义一个 classmethod
. In the sample code below, I've simply named it make()
. It's analogous to the class method named _make()
✶ that collections.namedtype
子 class 来避免定义 __new__()
。
✶这是向任何 class.
提供“替代构造函数”的常用方法
请注意,我还更改了对函数的第一次调用,以便将参数正确传递给 make()
方法。
from typing import NamedTuple
class TicTacToe(NamedTuple):
"""A tic-tac-toe board, each character is ' ', 'x', 'o'."""
row1: str = ' '
row2: str = ' '
row3: str = ' '
@classmethod
def make(cls, *args, **kwargs):
print(f'Enter make() with {cls}, {args}, {kwargs}')
if len(args) == 1 and args[0] == 0:
new_args = (' ', ' ', ' ')
else:
new_args = args
self = cls(*new_args, *kwargs)
return self
if __name__ == '__main__':
# a = TicTacToe.make(('xo ', 'x x', 'o o'))
a = TicTacToe.make('xo ', 'x x', 'o o')
print(a)
b = TicTacToe.make(0)
print(b)
输出:
Enter make() with <class '__main__.TicTacToe'>, ('xo ', 'x x', 'o o'), {}
TicTacToe(row1='xo ', row2='x x', row3='o o')
Enter make() with <class '__main__.TicTacToe'>, (0,), {}
TicTacToe(row1=' ', row2=' ', row3=' ')
更新
无法重载 NamedTuple
subclass' __new__()
方法的另一种解决方法是将派生的 class 拆分为两个 classes,一个public和一个private,所以前者不再是NamedTuple
.
的直接subclass
这样做的一个好处是,不再需要使用像上面 make()
这样的特殊用途 class 方法来创建实例。
我的意思是:
from typing import NamedTuple
class _BaseBoard(NamedTuple):
"""Private base class for tic-tac-toe board."""
row1: str = ' '
row2: str = ' '
row3: str = ' '
class TicTacToe(_BaseBoard):
"""A tic-tac-toe board, each character is ' ', 'x', 'o'."""
__slots__ = () # Prevent creation of a __dict__.
@classmethod
def __new__(cls, *args, **kwargs):
print(f'Enter __new__() with {cls}, {args}, {kwargs}')
if len(args) == 1 and args[0] == 0:
new_args = (' ', ' ', ' ')
else:
new_args = args
self = super().__new__(*new_args, *kwargs)
return self
if __name__ == '__main__':
a = TicTacToe('xo ', 'x x', 'o o')
print(a)
assert getattr(a, '__dict__', None) is None # Verify not being created.
b = TicTacToe(0)
print(b)
请注意,此方法是应用 Andrew Koenig 的 fundamental theorem of software engineering 的示例,即:“我们可以通过引入额外的间接级别来解决任何问题。”
我正在构建一个可以接受不同初始化方式的 typing.NamedTuple class(see typing.NamedTuple docs here, or the older collections.namedtuples docs 它继承自)。
为什么在这种情况下使用 NamedTuple?我希望它是不可变的和可自动散列的,这样它就可以成为字典键,而且我不必编写散列函数。
我知道我需要使用 __new__
而不是 __init__
,因为 NamedTuples 是不可变的(例如,__new__
.
这是我当前的代码:
from typing import NamedTuple
class TicTacToe(NamedTuple):
"""A tic-tac-toe board, each character is ' ', 'x', 'o'"""
row1: str = ' '
row2: str = ' '
row3: str = ' '
def __new__(cls, *args, **kwargs):
print(f'Enter __new__ with {cls}, {args}, {kwargs}')
if len(args) == 1 and args[0] == 0:
new_args = (' ', ' ', ' ')
else:
new_args = args
self = super().__new__(cls, *new_args, *kwargs)
return self
if __name__ == '__main__':
a = TicTacToe(('xo ', 'x x', 'o o'))
print(a)
b = TicTacToe(0)
print(b)
但我收到以下错误:
Traceback (most recent call last):
File "c:/Code/lightcc/OpenPegs/test_namedtuple.py", line 4, in <module>
class TicTacToe(NamedTuple):
File "C:\Dev\Python37\lib\typing.py", line 1384,
in __new__
raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
AttributeError: Cannot overwrite NamedTuple attribute __new__
我是否无法为继承自 NamedTuple 的子 class 创建单独的 __new__
函数?从消息中可以看出,它正试图直接覆盖 NamedTuple 的 __new__
,而不是 TicTacToe class.
这是怎么回事?
您可以通过定义一个 classmethod
. In the sample code below, I've simply named it make()
. It's analogous to the class method named _make()
✶ that collections.namedtype
子 class 来避免定义 __new__()
。
✶这是向任何 class.
提供“替代构造函数”的常用方法请注意,我还更改了对函数的第一次调用,以便将参数正确传递给 make()
方法。
from typing import NamedTuple
class TicTacToe(NamedTuple):
"""A tic-tac-toe board, each character is ' ', 'x', 'o'."""
row1: str = ' '
row2: str = ' '
row3: str = ' '
@classmethod
def make(cls, *args, **kwargs):
print(f'Enter make() with {cls}, {args}, {kwargs}')
if len(args) == 1 and args[0] == 0:
new_args = (' ', ' ', ' ')
else:
new_args = args
self = cls(*new_args, *kwargs)
return self
if __name__ == '__main__':
# a = TicTacToe.make(('xo ', 'x x', 'o o'))
a = TicTacToe.make('xo ', 'x x', 'o o')
print(a)
b = TicTacToe.make(0)
print(b)
输出:
Enter make() with <class '__main__.TicTacToe'>, ('xo ', 'x x', 'o o'), {}
TicTacToe(row1='xo ', row2='x x', row3='o o')
Enter make() with <class '__main__.TicTacToe'>, (0,), {}
TicTacToe(row1=' ', row2=' ', row3=' ')
更新
无法重载 NamedTuple
subclass' __new__()
方法的另一种解决方法是将派生的 class 拆分为两个 classes,一个public和一个private,所以前者不再是NamedTuple
.
这样做的一个好处是,不再需要使用像上面 make()
这样的特殊用途 class 方法来创建实例。
我的意思是:
from typing import NamedTuple
class _BaseBoard(NamedTuple):
"""Private base class for tic-tac-toe board."""
row1: str = ' '
row2: str = ' '
row3: str = ' '
class TicTacToe(_BaseBoard):
"""A tic-tac-toe board, each character is ' ', 'x', 'o'."""
__slots__ = () # Prevent creation of a __dict__.
@classmethod
def __new__(cls, *args, **kwargs):
print(f'Enter __new__() with {cls}, {args}, {kwargs}')
if len(args) == 1 and args[0] == 0:
new_args = (' ', ' ', ' ')
else:
new_args = args
self = super().__new__(*new_args, *kwargs)
return self
if __name__ == '__main__':
a = TicTacToe('xo ', 'x x', 'o o')
print(a)
assert getattr(a, '__dict__', None) is None # Verify not being created.
b = TicTacToe(0)
print(b)
请注意,此方法是应用 Andrew Koenig 的 fundamental theorem of software engineering 的示例,即:“我们可以通过引入额外的间接级别来解决任何问题。”