определить свойство (с декоратором) условно (например, попробовать / кроме?) - PullRequest
0 голосов
/ 26 октября 2018

Я ищу способ определения свойств в Python.Идея состояла в том, чтобы определить свойство внутри блока try / кроме

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)

[РЕДАКТИРОВАТЬ 2] У меня есть метод внутри родителя, который позволяет мне знать, какие свойства.Для примера давайте предположим, что у меня есть метод ListAttributes в классе SomeOtherObj C ++, который возвращает список имен атрибутов (строк), динамически создаваемых классом Parent.

Ответы [ 3 ]

0 голосов
/ 26 октября 2018

Как насчет этого:

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
0 голосов
/ 26 октября 2018

Первая точка: синтаксис @decorator не является чем-то волшебным, это просто синтаксический сахар, поэтому

@decorator
def func():
    pass

на самом деле просто удобное сокращение для:

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

Вторая точка:тип property является обобщенной (и довольно простой - и здесь опять-таки никакой магией) реализацией протокола descriptor .Дескрипторы «работают» только тогда, когда они разрешены как атрибуты класса, поэтому установка свойств в качестве атрибутов экземпляра не будет работать, точка (точка поиска их вернет сам объект дескриптора, он не вызовет методы дескриптора __get__ и __set__).

Третий пункт: ваш код даже не устанавливает созданные свойства как атрибуты экземпляра (которые в любом случае не будут работать), это просто простые локальные имена, которые существуют только во время метода _CreateAttributesвыполнение.

Ничто из этого не решит вашу проблему - вам нужно предоставить больше контекста, чтобы кто-то мог опубликовать решение, гарантирующее работу для вашего конкретного варианта использования (в зависимости от того, как на самом деле реализован этот родительский класс C ++ и т. Д.) -но, по крайней мере, теперь вы знаете, почему ваши попытки терпят неудачу.

РЕДАКТИРОВАТЬ:

У меня есть метод внутри родителя, который позволяет мне узнать, какие свойства.Для примера давайте предположим, что у меня есть метод ListAttributes в классе SomeOtherObj C ++, который возвращает список имен атрибутов (строк), динамически создаваемых классом Parent.

Если это метод класса илиstaticmethod, вы можете использовать декоратор класса для создания свойств (или, чтобы уменьшить шаблон, пользовательский дескриптор):

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__ является установщиком атрибута по умолчанию, так что выубедитесь, что вы используете это с умом.

0 голосов
/ 26 октября 2018

Вы можете думать о классах Python как о пространстве имен для кода со специальной привязкой.Это то, что вы можете написать практически что угодно в теле класса (подумайте цикл, для дополнительного безумия), и это будет выполнено при импорте класса (в действительности, при импорте модуля).Единственная проблема, с которой вы столкнулись, заключается в том, что у вас нет простого доступа к родительским классам при закрытии дочерних классов (без использования метаклассов).

Так что, если вам нужно сделать быстрое и грязное исправление, вы можете сделать try/except like:

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/else или try/except только потому, что в каком-то родительском классе где-то отсутствует метод.И эта часть не может быть исправлена ​​так, как у вас сейчас.

Итак, главное рассмотреть - какова ситуация, когда поведение родительского класса неизвестно?Если он известен пользователям во время установки, рассмотрите возможность предоставления двух разных дочерних классов, никаких сюрпризов внутри.

Если он известен только во время выполнения, зачем даже проверять это?Пользователи по-прежнему будут использовать

try:
    child_obj.my_property
except AttributeError:
    ...

для перехвата вашего нестабильного интерфейса, но самое интересное, что отсутствие родительского метода получения приведет к тому же исключению, поэтому простой

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

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

будет использоваться практически так же

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...