Python 从内置对象继承时如何分配给基类对象?

Python 从内置对象继承时如何分配给基类对象?,python,inheritance,built-in,Python,Inheritance,Built In,我正在尝试扩展Python内置的list对象以包含元数据。在某些情况下,为了提高效率,我希望能够通过引用来分配基类对象 例如: class meta(list): def __init__(self, data=None, metadata=None): if data is not None: super().__init__(data) # performs a copy of data else: su

我正在尝试扩展Python内置的list对象以包含元数据。在某些情况下,为了提高效率,我希望能够通过引用来分配基类对象

例如:

class meta(list):
    def __init__(self, data=None, metadata=None):
        if data is not None:
            super().__init__(data) # performs a copy of data
        else:
            super().__init__()
        self.metadata = metadata

    def foo(self):
        new_data = [ i for i in range(10) ]
        return meta(new_data, "my meta data")
这正如预期的那样有效。调用
foo
返回一个新的
meta
对象,其中的值为0-9。但是,由于对基类初始值设定项的调用,在列表理解中创建并分配给
new_data
的列表会复制到meta的初始值设定项中。此附加副本是不必要的,因为它可以简单地将新_数据的引用分配给继承的列表对象,因为在退出
foo
后,不可能存在对它的其他引用

我想描述的是这样的:

class meta(list):
    def __init__(self, data=None, metadata=None):
        if data is not None:
            super().__init__(data) # performs a copy of data
        else:
            super().__init__()
        self.metadata = metadata

    def foo(self):
        result = meta(None, "my meta data") # an efficient initialization
        new_data = [ i for i in range(10) ]
        super(list, result) = new_data # just assign the reference instead of copying
        return result
但我知道这在语法上是不正确的。然而,它确实描述了我试图实现的目标。其目的是,新的
meta
对象通过将引用分配给基础列表对象来引用
new_data
列表对象

我知道我可以使用列表成员对象而不是从列表继承,但这会导致其他效率低下,因为现在所有的列表属性都必须在
meta
类中定义,并且会被包装在另一层函数调用中

所以…我的问题是:

  • 有没有办法做到这一点
  • 我可以作为子类中的独立对象访问底层对象吗
  • 它可以干净地实现而不产生比我试图消除的更多的开销吗
  • 是否有一些晦涩难懂的
    \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
    方法不是该语言的一个未记录的“特性”

元数据的实例必然是一个新实例,您可以指定给名称,而不是对象。不能简单地将新实例替换为
list
的实例。也就是说,
meta
的新实例不包含对另一个
list
实例的引用;它是list实例,只是带有一个
\uuuu class\uuuu
属性,该属性指向
meta
,而不是
list

如果不想复制参数,首先不要制作列表参数

class meta(list):
    def __init__(self, data=None, metadata=None):
        if data is not None:
            super().__init__(data) # performs a copy of data
        else:
            super().__init__()
        self.metadata = metadata

    def foo(self):
        new_data = range(10)
        return meta(new_data, "my meta data")
list.\uuuu init\uuuuuu
不是一个execking列表;它只需要一个iterable,对其元素的引用可以添加到刚构建的列表中

您可能无论如何都要覆盖
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,因为
列表中已经包含了决定是否有一个可传递给<

class meta(list):
    def __new__(cls, *args, metadata=None, **kwargs):
        new_list = super().__new__(cls, *args, **kwargs)
        new_list.metadata = metadata
        return new_list

    def foo(self):
        return meta(range(10), metadata="my meta data")

(最后,
foo
应该是一个静态方法或类方法,因为它没有使用调用它的
meta
实例。)

尽管@chepner没有直接回答我的问题,但我得到了他们的一些鼓励,并提出了以下解决方案

class meta_add_iterable:
    def __init__(self, *data):
        self.data = data # copies only the references to the tuple of arguments passed in
        self.current = -1

    def __iter__(self):
        return self

    def __next__(self):
        self.current += 1
        if self.current < len(self.data[0]):
            return sum([ i[self.current] for i in self.data ])
        raise StopIteration

class meta(list):
    def __init__(self, data, metadata):
        super().__init__(data)
        self.metadata = metadata

    def __add__(self, other):
        return meta(meta_add_iterable(self, other), self.metadata)

a = meta([1, 2, 3, 4, 5], "meta data")
b = meta([6, 7, 8, 9, 0], "more data")
a + b
[7, 9, 11, 13, 5]
考虑有两个
meta
对象,我们希望逐个元素将底层
list
对象添加到一起

最直截了当的答案与我原来的帖子相似。我将把错误检查留给读者来实现

class meta(list):
    def __init__(self, data, metadata):
        super().__init__(data)
        self.metadata = metadata

    def __add__(self, other):
        return meta([self[i] + other[i] for i in range(len(self))], self.__metadata)

a = meta([1, 2, 3, 4, 5], "meta data")
b = meta([6, 7, 8, 9, 0], "more data")
a + b
[7, 9, 11, 13, 5]
注意:元数据只是用来完成示例的,我知道我遗漏了确保b列表不短于a列表的检查。然而,该行动如期进行。也很容易看到列表理解创建的
列表
稍后在调用
list时被完全复制到
meta.\uuuu init\uuuu
中。\uuu init\uuuuu
通过
super()。\uuu init\uuuuuu(…)
调用。这第二份是我想要避免的

多亏@chepner识别了
列表。_init__
使用iterable,我想出了以下解决方案

class meta_add_iterable:
    def __init__(self, *data):
        self.data = data # copies only the references to the tuple of arguments passed in
        self.current = -1

    def __iter__(self):
        return self

    def __next__(self):
        self.current += 1
        if self.current < len(self.data[0]):
            return sum([ i[self.current] for i in self.data ])
        raise StopIteration

class meta(list):
    def __init__(self, data, metadata):
        super().__init__(data)
        self.metadata = metadata

    def __add__(self, other):
        return meta(meta_add_iterable(self, other), self.metadata)

a = meta([1, 2, 3, 4, 5], "meta data")
b = meta([6, 7, 8, 9, 0], "more data")
a + b
[7, 9, 11, 13, 5]
class meta\u add\u iterable:
定义初始化(自,*数据):
self.data=data#只复制对传入参数元组的引用
自身电流=-1
定义(自我):
回归自我
定义下一个(自我):
自身电流+=1
如果自电流
在上述实现中,在
meta.\uuuu add\uuu
方法中没有通过列表理解创建的临时列表。相反,我们只需将两个
meta
对象(
self
other
)传递给迭代器对象。这里唯一要做的复制是将引用复制到原始元对象,以便迭代器可以引用它们。然后对
meta.\uuuu init\uuuu
的调用传递迭代器,而不是已经创建的列表。
list.\uuuuu init\uuuuu
方法只是从该迭代器创建列表,这意味着每个引用的元对象仅被访问一次以检索其数据,并且结果仅在
列表中写入一次。此实现中完全省略了次副本,因为添加操作实际上被推迟到在
列表中调用迭代器
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

class meta(list):
    def __new__(cls, *args, metadata=None, **kwargs):
        new_list = super().__new__(cls, *args, **kwargs)
        new_list.metadata = metadata
        return new_list

    def foo(self):
        return meta(range(10), metadata="my meta data")
最好的部分是,我们不需要检查是否从列表对象或迭代器初始化

我知道现在有很多事情会出错。我省略了所有的错误检查,因此读者可以看到这个过程

我不确定使用@chepner建议的
\uuuu new\uuuu
方法实现它是否会更好。就我个人而言,我看不出有什么好处。也许@chepner