继承元组作为列表

4

我刚刚在写一个程序,其中有一个类继承自tuple。然而,当我将参数作为列表传递时,它仍然可以工作。以下是我的一些代码:

class Board(tuple):    
    def __init__(self, tup):
        print(type(tup))
        super().__init__()

board = Board([[Cell(False) for i in range(10)] for j in range(10)])
print(type(board))

这个程序然后输出:
<class 'list'>
<class '__main__.Board'>

我的问题是:为什么当我说我要传递一个 tuple 时,Python 允许我传递一个 list 作为参数?


4
你说你会在哪里传递一个元组? - thefourtheye
1
我想要对那些对问题进行负面评价的人进行反对投票。这个问题写得很好。 - dansalmo
就像 tuple([]) 是有效的一样,Board([]) 也是有效的(对于任何列表,不仅仅是空列表)。 - chepner
6个回答

5
def __init__(self, tup):

这就像其他函数一样。你已经为 tup 参数传递了一个列表。在Python中,没有办法限制参数的类型。子类化tuple只意味着你从tuple继承属性,而不是只能接受元组作为参数。

编辑:如果你非常想确保只接受元组,出于某种原因。你可以这样抛出异常。

class Board(tuple):
    def __init__(self, tup):
        if not isinstance(tup, tuple):
            raise TypeError("Constrcutor argument to Board should be a tuple")
        super().__init__()

print Board([1, 2])

输出

TypeError: Constrcutor argument to Board should be a tuple

tuplelist的性能比较

class Board(tuple):
    def __init__(self, tup):
        for i in tup:
            pass
myList, myTuple = range(10), tuple(xrange(10))

from timeit import timeit
print timeit("Board(myList)", "from __main__ import myList, Board", number = 1000000)
print timeit("Board(myTuple)", "from __main__ import myTuple, Board", number = 1000000)

输出

0.44806599617
0.413192987442

这清楚地显示传递一个list作为参数比tuple稍微慢一点,但几乎可以忽略不计,它们的性能几乎相等。

那么,如果我使用列表作为参数,我的程序是否同样快速高效? - LazySloth13
@Lewis 从技术上讲,tuplelist 稍微快一点,因为它们是不可变的。但对于少量元素来说,这种收益几乎可以忽略不计。 - thefourtheye
那么,因为我传递了一个元组,速度会有所损失吗?我是否最好像这样在语句中添加 tuple()board = Board(tuple([[Cell(False) for i in range(10)] for j in range(10)])) - LazySloth13
@Lewis,请看一下我回答中的性能比较。 - thefourtheye

1

您没有说明您期望tup是元组。您明确传入了一个列表,而在Python中参数不进行类型检查。您可以像这样限制参数的类型:

if type(tup) is not type(()):
    raise ValueError('Expected a tuple')

然而,这并不是必须的。Pythonic 的做法并不关心你拥有的对象类型,而是关心它是否支持你想要执行的操作。

1
< p > 元组 描述的是棋盘本身的内容,而不是传递给初始化程序的任何参数的内容。

初始化程序的参数不一定代表对象本身;它们可以是新对象的参数之类的东西。鉴于您的函数实际上并没有对输入执行任何操作,因此您可以传递任何内容。


0

列表和元组通常可以互换使用作为序列。

如果您尝试将其用作哈希,则列表和元组之间的区别变得重要:

class Board(tuple):    
    def __init__(self, tup):
        print(type(tup))
        d={}
        d[tup]='test'          # this will fail with an unhashable type, like a list
        super().__init__()

b1=Board(tuple([1,2,3]))
b2=Board([1,2,3])

输出:

<class 'tuple'>
<class 'list'>
Traceback (most recent call last):
  File "Untitled 8.py", line 9, in <module>
    b2=Board([1,2,3])
  File "Untitled 8.py", line 5, in __init__
    d[tup]='test'
TypeError: unhashable type: 'list'

通常情况下,Python 中没有类型检查。它是一种 EAFP 语言。
如果您想要更多“正式”的区分和一些关于类型子类化的正式检查,请考虑使用 collections.abc 而不是基本类型。

0
如果您想让__init__只接受tuple作为参数,可以按照以下方式操作:
class Board(tuple):    
    def __init__(self, tup):
        if not isinstance(tup, tuple):
            raise Exception('Invalid type')
        super().__init__()

但这是一个非常糟糕的想法。这不是Python的方式。


我不知道需求,我只看到原始代码 - 没有传递到超类。 - Konstantin Kovrizhnykh
嗯,你说得对,但我看除了将其传递上去之外,没有其他用途。楼主是初学者,可能已经忘记了这一点。 - glglgl

-1

你的代码基本上是这样做的。

>>> tuple([1,2,3])  # tuple() takes a list
(1, 2, 3)           # turns it into a tuple

tuple继承不会限制类的参数为tuple;它仅包含与tuple实例相同的方法。


实际上,它甚至不会这样做 - tup 除了传递给 type() 之外没有任何地方使用。当涉及到 Board 对象的状态时,它实际上是一个未使用的参数。 - cdhowie
它被传递到板的初始化函数中。 - Inbar Rose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接