有一种涉及子分类的情况我无法弄清楚。
我正在进行子分类Random(原因超出了重点)。这是我所拥有的一个基本示例:
import random
class MyRandom(random.Random):
def __init__(self, x): # x isn't used here, but it's necessary to show the problem.
print("Before")
super().__init__() # Nothing passed to parent
print("After")
MyRandom([])
Run Code Online (Sandbox Code Playgroud)
上面的代码在运行时给出以下错误(并且不打印“Before”):
>>> import test
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\_\PycharmProjects\first\test.py", line 11, in <module>
MyRandom([])
TypeError: unhashable type: 'list'
Run Code Online (Sandbox Code Playgroud)
对我来说,这没有任何意义。不知何故,即使我没有传递它,参数MyRandom显然也被直接传递给Random.__init__,并且该列表被视为种子。"Before"从不打印,所以显然我的初始化器甚至从未被调用。
我认为这可能是由于Random在 C 中实现的父级造成的,这导致了奇怪的情况,但是list子类化的类似情况不会产生错误,指出整数不可迭代:
class MyList(list):
def __init__(self, y):
print("Before")
super().__init__()
print("After")
r = MyList(2) # Prints "Before", "After"
Run Code Online (Sandbox Code Playgroud)
我不知道如何解决这个问题。我很少进行子类化,更罕见的是我对内置函数进行子类化,所以我一定在我的知识中出现了漏洞。这不是我期望子类化的工作方式。如果有人能解释这里发生了什么,我将不胜感激。
Python 3.9
实例化一个类会导致__new__调用它的方法。它在构造函数调用中传递类的名称和参数。所以MyRandom([1, 2])结果是调用MyRandom.__new__(MyRandom, [1, 2]). (3.9.10 文档)。
因为没有MyRandom.__new__()方法,所以会搜索基类。 random.Random确实有一个__new__()方法(参见random_new()_randommodule.c )。所以我们接到这样的电话random_new(MyRandom, [1, 2])。
查看 的 C 代码random_new(),它调用random_seed(self, [1, 2]). 由于第二个参数不是 Null、orNone或intint 的子类,因此代码调用PyObject_Hash([1, 2]). 但列表不可散列,因此会出现错误。
如果__new__()返回类的实例,则__init__()使用构造函数调用中的参数来调用该方法。
一种可能的解决方法是定义一个MyRandom.__new__()方法,该方法调用super().__new__()但仅传递适当的参数。
class MyRandom(random.Random):
def __new__(cls, *args, **kwargs):
#print(f"In __new__: {args=}, {kwargs=}")
# Random.__new__ expects an optional seed. We are going to
# implement out own RNG, so ignore args and kwargs. Pass in a
# junk integer value so that Random.__new__ doesn't waste time
# trying to access urandom or calling time to initialize the MT RNG
# since we aren't going to use it anyway.
return super().__new__(cls, 123)
def __init__(cls, *args, **kwargs):
#print(f"In __init__: {args=}, {kwargs=}")
# initialize your custom RNG here
pass
Run Code Online (Sandbox Code Playgroud)
还可以重写以下方法:random()、seed()、getstate()、setstate()和(可选)getrandbits()。
__init__()另一种修复方法是仅在子类的方法中使用关键字参数。random_new()用于检查是否正在创建 random.Random 实例的C 代码。如果为 true,则如果存在任何关键字参数,代码将引发错误。但是,如果正在创建子类,则任何关键字参数都会被 忽略random_new(),但可以在子类中使用__init__()。
class MyRandom(random.Random):
def __init__(self, *, x): # make x a keyword only argument
print("Before")
super().__init__() # Nothing passed to parent
print("After")
MyRandom(x=[])
Run Code Online (Sandbox Code Playgroud)
有趣的是,在 Python 3.10 中,代码已更改为在提供超过 1 个位置random_new参数时引发错误。
| 归档时间: |
|
| 查看次数: |
127 次 |
| 最近记录: |