属性子集的只读数据视图(非副本) - python

我想要所选属性的数据的只读视图(而不是副本)。我知道这可以通过描述符/或属性来解决,但到目前为止我还不知道如何解决。

如果有更好的方法/模式来解决这个问题,我很乐意学习。

class Data:
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c


class View:
    def __init__(self, data, attributes):
        self.attributes = attributes
        self.data = data
        for a in attributes:
            #setattr(self, a, lambda: getattr(data, a))
            setattr(self, a, property(lambda: getattr(data, a)))


    #@property
    #def b(self):
    #    return self.data.b

    def __getattr__(self, item):
        if item in self.attributes:
            return getattr(self.data, item)
        raise AttributeError("can't get attribute")

def test_view():
    data = Data(1, 2, 3)

    mydata = View(data, ['b', 'c'])  # but not a!
    assert mydata.b == 2

    data.b = 9
    assert mydata.b == 9

    with pytest.raises(AttributeError, match="can't set attribute"):
        mydata.b = 10

python参考方案

我知道这可以通过描述符/或属性来解决,但到目前为止我还不知道如何解决。

这是不正确的,实际上。

描述符仅在类上找到时才起作用,而不是在实例上起作用(属性是描述符的一种类型,因此此处没有区别)。由于视图将属性定义为实例数据,因此您无法为这些属性生成属性并将其粘贴在您的View实例上。所以setattr(self, a, property(lambda: getattr(data, a)))不起作用,不。

这不是用描述符解决的问题。坚持使用__getattr__进行查找,并使用相应的__setattr__方法来防止向视图添加属性:

class View:
    def __init__(self, source, *attrs):
        self._attrs = set(attrs)
        self._source = source

    def __getattr__(self, name):
        if name in self._attrs:
            return getattr(self._source, name)
        raise AttributeError(f"{type(self).__name__!r} object has no attribute {name!r}")

    def __setattr__(self, name, value):
        # special case setting self._attrs, as it may not yet exist
        if name == "_attrs" or name not in self._attrs:
            return super().__setattr__(name, value)
        raise AttributeError("can't set attribute")

    def __dir__(self):
        return self._attrs

我在这里做了一些修改。属性存储为一组,因此可以测试名称是否是构成视图的属性的一部分,我们可以高效地进行操作。我还实现了__dir__,因此dir(mydata)返回可用属性。

请注意,我还在此处稍作更改了API,使View()可以使用任意数量的参数来定义属性名称。这将使您的测试如下所示:

data = Data(1, 2, 3)

mydata = View(data, 'b', 'c')  # but not a!
assert mydata.b == 2

data.b = 9
assert mydata.b == 9

with pytest.raises(AttributeError, match="can't set attribute"):
    mydata.b = 10

实际上,即使使用元类,也无法为此即时生成描述符,因为实例的属性查找不会在元类上查询__getattribute____getattr__(这是一种优化,请参见Special method lookup )。仅在类上定义的__getattributes____getattr__保留为挂钩点,并且在这两种方法中的任何一个用于绑定它们的属性对象生成的间接性都比此处需要的多。

如果要创建许多这些View对象,则可能确实要使用__slots__。如果使用此选项,则不会为您的类创建__dict__描述符,因此实例不会具有任意属性。取而代之的是,命名每个属性可以拥有的属性,Python将为这些属性创建特殊的描述符,并为其值保留空间。因为赋予实例任意属性所需的__dict__词典要比为已知数量的属性保留的固定空间占用更多的内存空间,所以可以节省内存。这也带来了副作用,即您无法向View实例添加任何新属性,从而使__setattr__方法变得不必要:

class View:
   __slots__ = ("_attrs", "_source")

   def __init__(self, source, *attrs):
       self._attrs = set(attrs)
       self._source = source

   def __getattr__(self, name):
       if name in self._attrs:
           return getattr(self._source, name)
       raise AttributeError(f"{type(self).__name__!r} object has no attribute {name!r}")

   def __dir__(self):
       return self._attrs

但是,如果没有__setattr__,则尝试设置属性时在AttributeError上引发的消息确实会有所变化:

>>> mydata.b = 10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'View' object has no attribute 'b'

因此您可能仍要保留它。

Python sqlite3数据库已锁定 - python

我在Windows上使用Python 3和sqlite3。我正在开发一个使用数据库存储联系人的小型应用程序。我注意到,如果应用程序被强制关闭(通过错误或通过任务管理器结束),则会收到sqlite3错误(sqlite3.OperationalError:数据库已锁定)。我想这是因为在应用程序关闭之前,我没有正确关闭数据库连接。我已经试过了: connectio…

用大写字母拆分字符串,但忽略AAA Python Regex - python

我的正则表达式:vendor = "MyNameIsJoe. I'mWorkerInAAAinc." ven = re.split(r'(?<=[a-z])[A-Z]|[A-Z](?=[a-z])', vendor) 以大写字母分割字符串,例如:'我的名字是乔。 I'mWorkerInAAAinc”变成…

查找字符串中的行数 - python

我正在创建一个python电影播放器​​/制作器,我想在多行字符串中找到行数。我想知道是否有任何内置函数或可以编写代码的函数来做到这一点:x = """ line1 line2 """ getLines(x) python大神给出的解决方案 如果换行符是'\n',则nlines …

如何在PyQt4的动态复选框列表中检查stateChanged - python

所以我要从PyQt4的列表中添加复选框。但是我找不到在Window类中对每个状态使用stateChanged的方法。这是从列表元素添加它们的功能: def addCheckbox(self): colunas = Graphic(self.caminho).getColunas() for col in colunas: c = QtGui.QCheckBo…

如何每隔n秒运行一个函数,但又不中断其他代码?也许在后台运行? - python

Python是我的第一语言,因此如果没有意义,请理解我。我正在用python制作虚拟宠物。我将Tkinter用于GUI。我想每5秒在Action类中运行一次display方法。但是,如果我使用线程,时间表或时间并将代码放在按钮的正前方,它就会冻结并只运行显示方法。如何在不中断按钮和其他部件的情况下每5秒运行一次显示方法?class Pet: def __in…