简化通用属性创建

问题描述 投票:0回答:1

我是 Python 新手,我发现在制作课程时我一直需要编写

@property
def example(self):
    return self._example

@example.setter
def example(self, example):
     if #some conditions here:
          self._example = example

不幸的是,这似乎不起作用。

我正在开发一个项目,其中有十几个这样的项目,因此我尝试创建一个函数,该函数将采用属性名称和一些条件,并为其创建 getter、setter 和 deleter。

class Util:

    @classmethod
    def custom_property(cls, prop, conds=None, deletable=False):
        def getter(self):
            return getattr(self, f'_{prop}')

        def setter(self, value):
            if conds is None or conds(value):
                setattr(self, f'_{prop}', value)
            else:
                raise ValueError(f'Invalid value for {prop}')

        def deleter(self):
            if deletable:
                del self._value
            else:
                print("This attribute can't be deleted.")

        # setattr(target, prop, property(getter, setter, deleter))

        return [getter, setter, deleter]

这是我尝试使用它的一个例子:

from utils import Util

def name_conds(name):
    if not isinstance(name, str):
        raise TypeError("City name must be a string")
    if not name.strip():
        raise ValueError("City name cannot be blank")
    if not 2 <= len(name) <= 25:
        raise ValueError("City name length must be between 2 and 25 characters")
    return True

class City:

    def __init__(self, name):
        self._name = name

    [name_getter, name_setter, name_deleter] = Util.custom_property("name", name_conds)
    name = property(name_getter, name_setter, name_deleter)

不幸的是,这并不完全有效。如果我创建一个名为“asdf”的城市并尝试将其更改为“a”,我会得到正确的错误,即长度错误,但是如果我实例化一个名为“a”的新城市,我可以没有问题。人们肯定有办法像我一样绕过写出属性吗?另外,正如 Util 中的注释行所示,我希望能够通过在任何需要属性的类中调用 Util.custom_property 来以更快的方式使用该方法,但这将属性添加到了 Util 中(当然,因为它是将其添加到 cls)。似乎没有办法将 City 类作为 City 内部的参数,但是在 city 外部调用 custom_property 可能意味着 city 中的函数无法正确引用名称。

python oop properties
1个回答
1
投票

问题不在于您的财产,而在于您没有使用

City.__init__
中的财产。
_name
实际上是属性本身的私有实现细节(尽管该值存储在
City
的实例上),因此
City
方法也不应该直接使用它。

在回答问题时,我会指出

custom_property
不一定是类方法(或任何类的方法);它可以是一个普通的函数。此外,它还可以返回一个
property
实例本身,您可以将其直接分配给
City
的属性。最后,您可以通过定义一个在
setter
conds
时无条件设置值来优化
None
,并且如果
deletable
False
则可以省略删除器。

def custom_property(cls, prop, conds=None, deletable=False):
    def getter(self):
        return getattr(self, f'_{prop}')

    if conds is None:
        def setter(self, value):
            setattr(self, f'_{prop}', value)
    else:
        def setter(self, value):
            if conds(value):
                setattr(self, f'_{prop}', value)
            else:
                raise ValueError(f'Invalid value for {prop}')

    if deletable:
        def deleter(self):
            del self._value
    else:
        deleter = None

    return property(getter, setter, deleter)

然后

from utils import custom_property


def name_conds(name):
    if not isinstance(name, str):
        raise TypeError("City name must be a string")
    if not name.strip():
        raise ValueError("City name cannot be blank")
    if not 2 <= len(name) <= 25:
        raise ValueError("City name length must be between 2 and 25 characters")
    return True


class City:

    def __init__(self, name):
        self.name = name  # Use the property, not _name

    name = custom_property("name", name_conds)
© www.soinside.com 2019 - 2024. All rights reserved.