尝试使用元类创建只读插槽

2024-10-01 15:41:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我根据我读到的hereherehere写了一些代码。你知道吗

#! /usr/bin/env python3

class ROSlotsType(type):
    def __new__(cls, name, bases, namespace, **kwds):
        roprops = namespace.pop("__roslots__")
        namespace["__slots__"] = tuple(("_" + propname) for propname in roprops)
        for propname in roprops:
            namespace[propname] = property(lambda self: getattr(self, "_" + propname)) # can't use self.__dict__ since it doesn't exist
        return type.__new__(cls, name, bases, namespace)

class Location(metaclass = ROSlotsType):
    __roslots__ = ["lat", "lon", "alt"]
    def __init__(self, lat, lon, alt = 0):
        self._lat = lat ; self._lon = lon ; self._alt = alt
    def __repr__(self):
        return "Location({}, {}, {})".format(self._lat, self._lon, self._alt)

place = Location(25.282, 82.956, 77.0)
print("Created object {}".format(place))

print("Accessing its attributes:", place.lat, place.lon, place.alt)

print("Trying to access its __dict__...")
try: place.__dict__
except:
    print("Caught exception; object has only __slots__: {}".format(place.__slots__))

print("Trying to set new property...")
try: place.name = "Varanasi"
except:
    print("Caught exception; cannot add new property")

print("Trying to modify read-only property...")
try: place.alt += 1
except:
    print("Caught exception; cannot modify read-only property")

执行上述步骤可得到:

Created object Location(25.282, 82.956, 77.0)
Accessing its attributes: 77.0 77.0 77.0
Trying to access its __dict__...
Caught exception; object has only __slots__: ('_lat', '_lon', '_alt')
Trying to set new property...
Caught exception; cannot add new property
Trying to modify read-only property...
Caught exception; cannot modify read-only property

插槽和只读行为工作得很好,但显然属性getter有一些问题,因为直接使用_lat_lon__repr__给出了正确的值,而使用place.latplace.lon的属性访问却给出了place.alt的值。你知道吗

请告诉我我的代码有什么问题以及如何修复它。你知道吗


Tags: toselfonlynewexceptionplacepropertyalt
1条回答
网友
1楼 · 发布于 2024-10-01 15:41:50

这里的lambda创建了一个匿名函数:

namespace[propname] = property(lambda self: getattr(self, "_" + propname))

该函数引用propname,这是定义它的函数的局部变量。不幸的是,此时它没有复制propname值,而是对propname变量保留了一个引用,一旦开始实际使用该函数,for循环就完成了,propname剩下了roprops中的最后一个值,即alt。你知道吗

要解决这个问题,可以使用一种有点老套但被广泛认可的方法,通过值而不是引用来捕获它:创建一个隐藏另一个变量的参数,但是使用一个默认值来获取所需的值:

namespace[propname] = property(lambda self, propname=propname: getattr(self, "_" + propname))

作为Karl Knechtel mentions in the comments,您还可以使用^{},这样就完全消除了hacky位:

namespace[propname] = property(operator.attrgetter('_' + propname))

最后,由于您的问题最初发布在CodeReview上,我要注意您可能应该通过^{}运行代码。你知道吗

相关问题 更多 >

    热门问题