2014-05-17 41 views
0

我根據讀取的內容編寫了一些代碼hereherehere嘗試使用metaclasses創建只讀插槽

#! /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 

的插槽和只讀行爲做工精細,但顯然有一些問題與物業干將,因爲雖然__repr__它採用_lat_lon是直接給出正確的值時,使用place.latplace.lon的屬性訪問取而代之的值爲place.alt

請告訴我我的代碼有什麼問題以及如何解決它。

回答

3

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,你也可以使用operator.attrgetter,從而消除了哈克位乾脆:

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

最後,你的問題最初發布在代碼審查,我注意到你應該可能運行你的c通過pep8頌。

+2

在這種特殊情況下,我可以建議'namespace [propname] = property(operator.attrgetter('_'+ propname))'?這裏也值得一提'functools.partial',儘管不幸的是這次不是我們想要綁定的第一個參數。 –