Python: передать элемент члена класса одной из его функций-членов по ссылке - PullRequest
0 голосов
/ 22 апреля 2020

У меня есть некоторые функции внутри класса, которые работают с членами класса. Эти функции почти идентичны, кроме члена класса, с которым они работают. Чтобы избежать необходимости вводить функцию снова и снова, меняя только измененный член класса, я создал функцию, в которой одним из параметров является член класса, который нужно изменить.

Вот очень упрощенный версия:

class Foo:
    a = 0
    b = 0

    def __set_member(self, member, value):
        member = value

    def set_a(self, value):
        self.__set_member(self.a, value)

f = Foo()

print(f.a)
f.set_a(2)
print(f.a)

Но это выводит:

0
0

в C ++, это можно сделать с помощью:

class Foo {

    void set_member(int& member, int value) {
        member = value;
    }

public:
    int a = 0;
    int b = 0;

    void set_a(int value) {
        set_member(this->a, value);
    }
};

Очевидно, что приведенный мной пример не требуется функция set_member, потому что она очень проста.

В моем реальном коде сеттер выглядит примерно так:

def requestOpenDoor1(self):
    # Non blocking function: sets door1 to `Opening` and then after 2 seconds sets it to `Open`
    self.__door1 = DoorStates.Opening
    async def callback(): self.__door1 = DoorStates.Open
    self.__tasks.append(AsyncTimer(2, callback))

Один из подходов - скопировать и вставить эту функцию 20 или поэтому время и просто измените изменяемый член класса, состояние перехода, время и endState, но я бы предпочел уменьшить дублирование кода.

Я также пытался использовать декораторы для решения этой проблемы:

class request_function_decorator(object):
    def __init__(self, tasks, stateToChange, transitionState, time, endState):
        self.__tasks = tasks
        self.__stateToChange = stateToChange
        self.__transitionState = transitionState
        self.__time = time
        self.__endState = endState
    def __call__(self, function):
        def wrapped_f(*args):
            self.__stateToChange = self.__transitionState
            async def callback(): self.__stateToChange = self.__endState
            self.__tasks.append(AsyncTimer(self.__time, callback))
        return wrapped_f

@request_function_decorator(__tasks, __door1, States.Opening, 2, States.Open)
def requestOpenDoor1(self):
    pass

Но это также не работает.

Как можно обобщить эту функцию, чтобы уменьшить дублирование кода?

Спасибо.

Ответы [ 3 ]

1 голос
/ 22 апреля 2020

По умолчанию вы должны иметь __setattr__:

class Foo:
    a = 0
    b = 0

f = Foo()
print(f.a)
# 0

f.a = 9
print(f.a)
# 9

f.__setattr__("a", 99)
print(f.a)
# 99

setattr(f, "a", 900)
print(f.a)
# 900
0 голосов
/ 23 апреля 2020

Эквивалент Python вашего первого фрагмента довольно прост:

class Foo:
    def __init__(self):
        # instances attributes must be defined on the instance, 
        # not on the class.
        self.a = 0
        self.b = 0

    def set_a(self, a):
        setattr(self, "a", a)

f = Foo()

print(f.a)
f.set_a(2)
print(f.a)

Теперь обратите внимание, что здесь set_a это - с точки зрения Pythoni c - полный WTF: первый он абсолютно бесполезен (a - это атрибут publi c, вы можете установить его напрямую), тогда его реализация использует setattr без веской причины (вы можете просто реализовать его как self.a = a.

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

class Foo:
    def __init__(self):
        # instances attributes must be defined on the instance, 
        # not on the class.
        self.a = 0
        self.b = 0

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

    @a.setter 
    def a(self, a):
        if a == 42:
            raise HitchikerError("yes, but what is the question ?")  
        self._a = a

Также обратите внимание, что соглашение о каноническом именовании для Атрибуты реализации (атрибуты, которые не являются частью интерфейса класса publi c) - это одно ведущее подчеркивание, двойное подчеркивание (и система управления именами, которую он вызывает gers) предназначен только для защиты от случайных конфликтов имен в глубоких иерархиях наследования и практически не используется (потому что почти всегда необходим).

Надеюсь, что это поможет ...

0 голосов
/ 22 апреля 2020

Вы можете получить эффект, похожий на указатель, сделав переменные класса объектами непостоянного списка.

Видимо, кому-то здесь не нравится мой пример. То, что я демонстрирую здесь, не должно требоваться в типичном коде; однако, исходя из фона C / C ++, это может иметь некоторый смысл Он показывает аналогичный случай, когда мышление в терминах указателей имеет сходство с ссылкой на список по индексу и передачей его методам и функциям.

>>> class Foo:
...     _a = [0]   # Class vars.
...     _b = [0]
...     
...     def __init__(self):
...         self._c = [0]  # Instance var.
... 
...     def _set_member(self, member, value): 
...         # Some lengthy algorithm you don't want to 
...         # implement separately for each data member...
...         member[0] = value
... 
...     @property
...     def a(self):
...         return self._a[0]
...         
...     @a.setter
...     def a(self, value):
...         self._set_member(self._a, value)
...         
>>> f = Foo()
>>> f.a
0
>>> f.a = 22
>>> f.a
22
>>> 

C указатели также могут быть разыменованы с помощью нулевого индекса. int *a; ... a[0] = 1;.

Однажды при реализации интерфейса к нативной библиотеке с использованием API Python C мне нужно было написать оболочки для нескольких функций, для которых в качестве параметра использовалось несколько указателей. , Я хотел, чтобы списки параметров функций Python были близки к функциям C. Поэтому я использовал списки для этих параметров, чтобы имитировать c эффект указателя.

...