有条件地定义属性(使用装饰器)(例如try / except?)

时间:2018-10-26 08:23:04

标签: python oop properties

我正在寻找一种在python中有条件地定义属性的方法。 想法是在try / except块内定义属性

class PyObject(SomeOtherObj):

    def __init__(self, dbaObj):

        super(SomeOtherObj, self).__init__()

        self._CreateAttributes()

    def _CreateAttributes(self):

        try:
            self.GetProperty("MyProperty") #This method comes from the parent
        except:
            pass
        else:
            @property
            def MyProperty(self):
                return self.GetProperty("MyProperty")

            @MyProperty.setter
            def MyProperty(self, value):
                return self.ModifyProperty("MyProperty", value) #This method comes from the parent

我不知道在父对象中定义了哪些属性,因此我需要一些有条件地构建属性的东西。也欢迎任何其他解决方案。

[EDIT]另一种尝试...但是无论如何无论如何都会创建该属性,但是尝试访问它会引发无限递归错误

class PyObject(SomeOtherObj):

    def __init__(self, dbaObj):

        super(SomeOtherObj, self).__init__()

        @property
        def MyProperty(self):
            try:
                return self.GetProperty("MyProperty")
            except:
                del self.MyProperty

        @MyProperty.setter
        def MyProperty(self, value):
             return self.ModifyProperty("MyProperty", value) #This method comes from the parent

         @MyProperty.deleter
         def MyProperty(self):
              self.__delattr__(self.MyProperty)

[EDIT2]我在父级内部有一个方法,可以让我知道哪些是属性。出于示例的原因,我们假设我在ListAttributes C ++类中有一个方法SomeOtherObj,该方法返回一个由Parent类动态创建的属性名称(字符串)的列表。

3 个答案:

答案 0 :(得分:1)

您可以将python类视为具有特殊绑定的代码命名空间。是的,您可以在类主体中编写几乎所有内容(考虑一下循环,以提高疯狂程度),它将在类导入时执行(实际上是在模块导入中)。唯一的问题是,在子类关闭中您无法轻松访问父类(不使用元类)。

因此,如果您需要快速而又肮脏的补丁,实际上可以像这样制作try/except

class Child(Parent):
    try:
        Parent.get_property
        @property
        def my_property(self):
            return self.get_property()

        @my_property.setter
        def my_property(self, value):
            self.set_property(value)
    except:
        pass

第一个问题-父类是硬编码的,如果您更改继承,则需要更改实现。这实际上是不好的设计,但是如果您几乎没有约束,也许对您来说还可以。

更大的问题是,这种接口很难使用。在大多数情况下,如果您提供属性/方法,则您的用户将期望类具有此属性/方法。经常使用if/elsetry/except只是一个有趣的方法,因为某些父类缺少方法。而且这部分无法以您现在拥有的方式来固定。

因此,主要要考虑的问题-当父类行为未知时,情况如何?如果用户在安装时就知道它,请考虑提供两个不同的子类,内部没有任何惊喜。

如果仅在运行时已知,为什么还要为此检查呢?用户仍将使用

try:
    child_obj.my_property
except AttributeError:
    ...

捕捉您的浮动界面,但有趣的是缺少父getter会产生相同的异常,所以很简单

class Child(Parent):
    @property
    def my_property(self):
        return self.get_property()

    @my_property.setter
    def my_property(self, value):
        self.set_property(value)

使用方式几乎相同

答案 1 :(得分:1)

第一点:@decorator语法没什么神奇的,只是语法糖,所以

@decorator
def func():
    pass

实际上只是用于以下目的的便捷快捷方式:

def func():
    pass
func = decorator(func)

第二点:property类型是the descriptor protocol的通用(并且相当简单-在这里,没有涉及魔术)。描述符仅在解析为类属性时才“起作用”,因此将属性设置为实例属性将无法正常工作(句点(查找它们将返回描述符对象本身,不会调用描述符的__get____set__)方法)。

第三点:您的代码甚至没有将创建的属性设置为实例属性(无论如何都无法工作),它们只是普通的 local 名称,仅在_CreateAttributes期间存在方法执行。

这些都不能解决您的问题-您必须提供更多上下文,以便某人发布专为您的具体用例工作的解决方案(基于此C ++父类的实际实现方式等)-但至少您现在知道尝试失败的原因了。

编辑:

  

我在父级内部有一个方法,可以让我知道哪些是属性。为了举例说明,假设我在SomeOtherObj C ++类中有一个ListAttributes方法,该方法返回由Parent类动态创建的属性名称(字符串)的列表。

如果这是类方法或静态方法,则可以使用类装饰器来创建属性(或者,为简化样例,使用自定义描述符):

class Parent(object):
    def __init__(self):
        self._props = {"foo": 42, "bar": None}

    @classmethod
    def ListAttributes(cls):
        return ["foo", "bar"]

    def GetProperty(self, name):
        return self._props[name]

    def ModifyProperty(self, name, value):
        self._props[name] = value


class Prop(object):
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, cls):
        return instance.GetProperty(self.name)

    def __set__(self, instance, value):
        instance.ModifyProperty(self.name, value)

def setprops(cls):
    parent = cls.__bases__[0]
    for name in parent.ListAttributes():
        setattr(cls, name, Prop(name))
    return cls

@setprops
class Child(Parent):
    pass


c = Child()
print("foo : {}".format(c.foo))
print("bar : {}".format(c.bar))

c.bar = "yay!"
print("bar: {}".format(c.bar))

如果Parent.ListAttributes是实例方法,那么您仍然可以使用__getattr____setattr__特殊方法:

class Parent2(object):
    def __init__(self):
        self._props = {"foo": 42, "bar": None}

    def ListAttributes(self):
        return ["foo", "bar"]

    def GetProperty(self, name):
        return self._props[name]

    def ModifyProperty(self, name, value):
        self._props[name] = value


class Child2(Parent2):
    def __getattr__(self, name):
        if name in self.ListAttributes():
            return self.GetProperty(name)
        raise AttributeError("object {} has no attribute {}".format(type(self), name))

    def __setattr__(self, name, value):
        if name in self.ListAttributes():
            self.ModifyProperty(name, value)
        super(Child2, self).__setattr__(name, value)



c = Child2()
print("foo : {}".format(c.foo))
print("bar : {}".format(c.bar))

c.bar = "yay!"
print("bar: {}".format(c.bar))

# check we didn't break the default __setattr__
c.baaz = "ok"
print("baaz: {}".format(c.baaz))

当心:__getattr__仅在所有其他查找均失败的情况下才作为最后手段使用,因此几乎无害,但__setattr__是默认的属性设置器,因此必须确保明智地使用它。

答案 2 :(得分:0)

那呢:

class A:
    def stuff(self):
        pass

a = A()   


if hasattr(a, 'another'):
    print(a.another)
else:
    A.another = property(lambda self: 1)
    print(a.another)

您可以通过在运行时中设置属性来简单地修补类

Result: 1