Python 3.4 - 如何为许多类创建模板属性

时间:2016-01-30 23:28:53

标签: class python-3.x object properties

在Python 3.x中,是否可以创建一个"模板"可以被许多班级重用的财产?

我有大约200个课程,对应于在商店出售的产品。每个类都使用特定于产品的属性。 "得到"和"让"当设置属性值时,每个属性的函数执行有时对数据进行精心的完整性检查。

有时,对许多不同类的许多不同属性执行相同的检查。唯一改变的是属性名称。下面提供了一个简化的例子,用于"机器人"和" Box"类。在这些类中,属性检查是否将其设置为小于1的值并进行更正。

class Robot():


   def setPrice(self,Price):              # <--                        
                                          # <--
      if (Price < 1): Price = 1           # <--
                                          # <-- Common check
      self.__Price = Price                # <-- to many
                                          # <-- classes
   def getPrice(self):                    # <--  
                                          # <-- 
      return self.__Price                 # <-- 
                                          # <--
   Price = property(getPrice, setPrice)   # <-- 


class Box():


   def setWeight(self,Weight):            # <--                        
                                          # <--
      if (Weight < 1): Weight = 1         # <--
                                          # <-- Common check
      self.__Weight = Weight              # <-- to many
                                          # <-- classes
   def getWeight(self):                   # <--  
                                          # <-- 
      return self.__Weight                # <-- 
                                          # <--
   Weight = property(getWeight, setWeight) # <-- 

是否可以将此属性隔离为某种类的外部函数,然后可以由我的许多类调用?换句话说,可以实现这样的事情吗?

class_or_function TemplateProperty():

   ... some code ...


class Robot():    

    Price = TemplateProperty()      
    NumberOfLegs = TemplateProperty()       

class Box():    

    Price = TemplateProperty() 
    Weight = TemplateProperty() 
    Height = TemplateProperty()
    Length = TemplateProperty()      

2 个答案:

答案 0 :(得分:0)

如果你的对象永远不会有多个模板化属性,那么你可以编写一个简单的函数来构建一个合适的属性对象并返回它:

def PositiveValuedProperty():
    def getter(obj):
        return obj.__value

    def setter(obj, new_value):
        if new_value < 1:
            new_value = 1
        obj.__value = new_value

    return property(getter, setter)

然后你的课程将是:

class Robot:
    Price = PositiveValuedProperty()

class Box:
    Weight = PositiveValuedProperty()

但是,如果任何类使用这些属性的多个,则不会起作用,因为它们都写入相同的属性名称。如果您想允许多个类在一个类中,您可能想要为工厂函数指定一个属性名称:

def PositiveValuedProperty(name):
    name = "_" + name

    def getter(obj):
        return getattr(obj, name)

    def setter(obj, new_value):
        if new_value < 1:
            new_value = 1
        setattr(obj, name, new_value)

    return property(getter, setter)

现在,您可以使用RobotBoxPrice设置Weight

def RobotBox():
    Price = PositiveValuedProperty("Price")
    Weight = PositiveValuedProperty("Weight")

如果您希望内部代码无需经过_Price检查即可访问,则实际值将存储在属性_Weightproperty中。

请注意,如果你的逻辑比上面的复杂得多,你可能会更好地构建自己的描述符类型,而不是创建闭包并将它们传递给property构造函数,就像我上面所做的那样。描述符只是一个类,其中包含根据descriptor protocol定义的一些__get____set__和/或__delete__方法。以下是如何将PositiveValuedProperty的最后一个版本实现为描述符类而不是工厂函数:

class PositiveValuedProperty():
    def __init__(self, name):
        self.name = "_" + name

    def __get__(self, obj, cls=None):
        return getattr(obj, self.name)

    def __set__(self, obj, new_value):
        if new_value < 1:
            new_value = 1
        setattr(obj, self.name, new_value)

正如您所看到的,它与property代码几乎完全相同,因为property是一个描述符,它使用您传递的函数来实现其__get__和{ {1}}方法。但是,您可以编写更复杂的描述符。例如,您可以通过检查__set__(或type(obj)参数到cls)的属性并查找其中的描述符来使您的描述符推断出自己的名称(如果您愿意)。等于__get__。这有点脆弱,因为描述符实际上可能有多个名称(例如self)并且搜索类的变量可能很慢。

答案 1 :(得分:0)

每个类一个描述符的简单解决方案

您可以使用descriptor

class MinValue:

    def __init__(self, limit=1):
        self.limit = limit

    def __get__(self, instance, cls):
        if not hasattr(instance, '_value'):
            return
        return instance._value

    def __set__(self, instance, value):
        if value < self.limit:
            value = self.limit
        instance._value = value

在课堂上使用它:

class Robot:
    price = MinValue()

class Box:
    weight = MinValue(10)

测试它:

>>> r = Robot()
>>> r.price = 0.5
>>> r.price
1

>>> b = Box()
>>> b.weight = 8
>>> b.weight
10

每个类的多个描述符的更复杂的解决方案

将实例存储在描述符中。为避免内存泄漏,因为您可能会在普通字典中保留对已删除实例的引用,请使用WeakKeyDictionary

from weakref import WeakKeyDictionary

class MinValue:
    def __init__(self, limit=1):
        self.limit = limit
        self._values = WeakKeyDictionary()

    def __get__(self, instance, cls):
        return self._values.get(instance)

    def __set__(self, instance, value):
        if value < self.limit:
            value = self.limit
        self._values[instance] = value

class Robot:
    price1 = MinValue()
    price2 = MinValue()

class Box:
    height = MinValue()
    width = MinValue()
    weight = MinValue()

测试它:

>>> robot = Robot()
... box = Box()
...
>>> robot.price1 = 10
... robot.price2 = .5
...
>>> robot.price1
10
>>> robot.price2
1
>>> b.height = 1
... b.width = 20
... b.weight = 0.4
...
>>> box.height = 1
... box.width = 20
... box.weight = 0.4
...
>>> box.height
1
>>> box.width
20
>>> box.weight
1