使用类实例作为类属性,描述符和属性

时间:2011-08-11 11:18:47

标签: python class properties descriptor new-style-class

我最近曾说过尝试在Python中使用较新的类(从对象派生的类)。作为熟悉它们的例外,我试图定义一个具有许多类实例作为属性的类,每个类实例描述不同类型的数据,例如: 1d列表,2d数组,标量等。基本上我希望能够写

some_class.data_type.some_variable

其中data_type是描述变量集合的类实例。下面是我第一次尝试实现这一点,只使用profiles_1d实例而非通用名称:

class profiles_1d(object):
    def __init__(self, x, y1=None, y2=None, y3=None):
        self.x = x
        self.y1 = y1
        self.y2 = y2
        self.y3 = y3

class collection(object):
    def __init__(self):
        self._profiles_1d = None

    def get_profiles(self):
        return self._profiles_1d

    def set_profiles(self, x, *args, **kwargs):
        self._profiles_1d = profiles_1d(x, *args, **kwargs)

    def del_profiles(self):
        self._profiles_1d = None

    profiles1d = property(fget=get_profiles, fset=set_profiles, fdel=del_profiles,
        doc="One dimensional profiles")

上述代码大致是解决此问题的适当方法。我看到使用property的例子只是设置了一些变量的值。在这里,我需要使用set方法初始化某个类的实例。如果没有,我们将非常感谢任何其他更好的实施方法的建议。

另外,我定义set方法的方式是否正常?通常,根据我的理解,set方法定义了用户键入时要执行的操作,在此示例中为

collection.profiles1d = ...

我可以使用上面的代码正确设置profiles_1d实例的属性的唯一方法是键入collection.set_profiles([...], y1=[...], ...),但我认为我不应该直接调用此方法。理想情况下,我想输入collection.profiles = ([...], y1=[...], ...):这是正确/可能吗?

最后,我看到一个装饰者提到了很多类型的新风格,但这是我所知道的很少。在这里使用装饰器是否合适?对于这个问题,我应该知道更多的事情吗?

2 个答案:

答案 0 :(得分:10)

首先,你正在学习新式课程。他们有很多优势。

在Python中创建属性的现代方法是:

class Collection(object):
    def __init__(self):
        self._profiles_1d = None

    @property
    def profiles(self):
        """One dimensional profiles"""
        return self._profiles_1d

    @profiles.setter
    def profiles(self, argtuple):
        args, kwargs = argtuple
        self._profiles_1d = profiles_1d(*args, **kwargs)

    @profiles.deleter
    def profiles(self):
        self._profiles_1d = None

然后通过

设置profiles
collection = Collection()
collection.profiles = (arg1, arg2, arg3), {'kwarg1':val1, 'kwarg2':val2}

注意所有三种方法具有相同的名称。

通常不会这样做;要么让他们将属性传递给collection构造函数,要么让他们自己创建profiles_1d,然后执行collections.profiles = myprofiles1d或将其传递给构造函数。

如果希望属性管理对自身的访问而不是管理对属性的访问的类,请将该属性设为具有描述符的类。如果与上面的属性示例不同,您实际上需要存储在属性中的数据(而不是另一个,faux-private实例变量),请执行此操作。此外,如果您要反复使用相同的属性,那么它很有用 - 使它成为描述符,您不需要多次编写代码或使用基类。

我实际上喜欢@ S.Lott的页面 - 用Python构建技能Attributes, Properties and Descriptors

答案 1 :(得分:1)

在创建需要调用其他实例方法的property(或其他描述符)时,命名约定是为这些方法添加_;因此,您上面的姓名应为_get_profiles_set_profiles_del_profiles

在Python 2.6+中,每个属性也是一个装饰器,因此您不必创建(否则无用)_name方法:

@property
def test(self):
    return self._test

@test.setter
def test(self, newvalue):
    # validate newvalue if necessary
    self._test = newvalue

@test.deleter
def test(self):
    del self._test

看起来你的代码试图在类而不是实例上设置profiles - 如果是这样的话,类上的属性将不起作用,因为collections.profiles将被覆盖{ {1}}对象,破坏属性......如果这真的是你想要的,你将不得不制作一个元类并将属性放在那里。

希望你在谈论实例,所以课程看起来像:

profiles_1d

然后你会做类似的事情:

class Collection(object):  # notice the capital C in Collection
    def __init__(self):
        self._profiles_1d = None

    @property
    def profiles1d(self):
        "One dimensional profiles"
        return self._profiles_1d

    @profiles1d.setter
    def profiles1d(self, value):
        self._profiles_1d = profiles_1d(*value)

    @profiles1d.deleter
    def profiles1d(self):
        del self._profiles_1d

需要注意的几件事情:collection = Collection() collection.profiles1d = x, y1, y2, y3 方法仅使用两个项目调用:setter和新self(这就是您必须调用value的原因}手动);在进行赋值时,关键字命名不是一个选项(仅适用于函数调用,而赋值不是)。如果它对你有意义,你可以得到一些喜欢并做一些事情:

set_profiles1d

然后将collection.profiles1d = (x, dict(y1=y1, y2=y2, y3=y3)) 更改为:

setter

仍然相当可读(虽然我自己更喜欢 @profiles1d.setter def profiles1d(self, value): x, y = value self._profiles_1d = profiles_1d(x, **y) 版本)。