【问题标题】:generating function based off class field?基于类场的生成函数?
【发布时间】:2019-12-24 21:46:18
【问题描述】:

我有一个类,它的字段都是属性,通过 getter 和 setter 以某种方式验证,这样它就会满足以下模式:

import numpy as np
import typing


def validate_field(value, dtype: typing.Type):
    limits = np.iinfo(dtype)
    assert limits.min < value < limits.max, \
        "value shoule be in range: {} < {} < {}".format(limits.min, value,
                                                        limits.max)
    return value


class Foo:
    def __init__(self, a, b, c):
        self._a = a
        self._b = b
        self._c = c

    @property
    def a(self):
        return self._a

    @property
    def b(self):
        return self._b

    @property
    def c(self):
        return self._c

    @a.setter
    def a(self, value):
        self._a = validate_field(value, self._a.dtype)

    @b.setter
    def b(self, value):
        self._b = validate_field(value, self._b.dtype)

    @c.setter
    def c(self, value):
        self._c = validate_field(value, self._c.dtype)

我想避免为每个方法键入单独的属性和设置器装饰器。

我想过通过手动使用属性

self._a = a
def set_a(self, value):
    self._a = validate_field(value, self._a.dtype)
self.a = property(lambda self: self._a, set_a)
...

但是,我似乎仍然需要手动定义一个函数来访问 setter 和 getter 所需的成员,所以我并没有真正节省太多工作。

如果有办法通过命名参数来自动生成这样的函数,例如:

def generate_function(self, parameter)
    def temp(self, value):
        self.parameter = validate_field(value, self.parameter.dtype)
    return temp

那么我不会有任何问题,但现在我不知道如何实现这一点。

有没有办法让我在__init__ 中使用每个字段的单个装饰器或基于自动函数的属性生成来生成这些函数?

【问题讨论】:

    标签: python python-3.x properties python-decorators


    【解决方案1】:

    您可以使用getattr()setattr(),或通过self.__dict__ 直接访问字典来参数化属性名称:

    def validated_property(name):
        def getter(self):
            return getattr(self, name)
    
        def setter(self, value):
            dtype = getter(self).dtype
            setattr(self, name, validate_field(value, dtype))
    
        return property(getter, setter)
    

    然后用这个作为

    class Foo:
        # ...
    
        a = validated_property('_a')
        b = validated_property('_b')
        c = validated_property('_c')
    

    等等

    如果您使用的是 Python 3.6 或更高版本,则可以避免重复属性名称并从属性名称生成一个(例如,通过在其前面加上 _ 前缀),通过 implementing your own descriptor object,即通过descriptor.__set_name__() method 传递了将其分配给类的名称:

    class ValidatedProperty:
        _name = None
    
        def __set_name__(self, owner, name):
            self._name = '_' + name
    
        def __get__(self, instance, owner):
            if instance is None:
                return self
            return getattr(instance, self._name)
    
        def __set__(self, instance, value):
            dtype = self.__get__(instance, type(instance)).dtype
            setattr(instance, self._name, validate_field(value, dtype))
    

    然后像这样使用:

    class Foo:
        # ...
    
        a = ValidatedProperty()
        b = ValidatedProperty()
        c = ValidatedProperty()
    

    【讨论】:

    • 你也可以在那里使用getter(self).dtype。如果 OP 喜欢冒险,他们可以使用a, b, c = [validated_property(name) for name in sorcery.assigned_names()]
    • @AlexHall:如果您要生成名称,只需使用生成器表达式并省去列表。
    猜你喜欢
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-05
    • 1970-01-01
    • 2019-10-11
    相关资源
    最近更新 更多