Python: передача функций в качестве аргументов для инициализации методов объекта.Питон или нет? - PullRequest
3 голосов
/ 29 марта 2019

Мне интересно, существует ли приемлемый способ передачи функций в качестве параметров объектам (т. Е. Для определения методов этого объекта в блоке init ).

Более конкретно, какМожно ли это сделать, если функция зависит от параметров объектов.

Кажется достаточно питоническим, чтобы передавать функции объектам, функции являются объектами, как и все остальное:

def foo(a,b):
    return a*b

class FooBar(object):
    def __init__(self, func):
        self.func = func

foobar = FooBar(foo)
foobar.func(5,6)

# 30

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

def foo1(self, b):
    return self.a*b

class FooBar1(object):
    def __init__(self, func, a):
        self.a=a
        self.func=func

# Now, if you try the following:
foobar1 = FooBar1(foo1,4)
foobar1.func(3)
# You'll get the following error:
# TypeError: foo0() missing 1 required positional argument: 'b'

Это может просто нарушать некоторые святые принципы ООП в Python, и в этом случае мне просто придется сделать что-то еще, нотакже кажется, что это может оказаться полезным.

У меня есть несколько возможных способов обойти это, и мне интересно, какой (если таковой имеется) считается наиболее приемлемым.

Решение 1

foobar1.func(foobar1,3)

# 12
# seems ugly

Решение 2

class FooBar2(object):
    def __init__(self, func, a):
        self.a=a
        self.func = lambda x: func(self, x)

# Actually the same as the above but now the dirty inner-workings are hidden away. 
# This would not translate to functions with multiple arguments unless you do some ugly unpacking.
foobar2 = FooBar2(foo1, 7)
foobar2.func(3)

# 21

Будут признательны за любые идеи!

Ответы [ 2 ]

1 голос
/ 29 марта 2019

Передача функций объекту в порядке.В этом дизайне нет ничего плохого.

Если вы хотите превратить эту функцию в связанный метод, вы должны быть немного осторожнее.Если вы делаете что-то вроде self.func = lambda x: func(self, x), вы создаете ссылочный цикл - self имеет ссылку на self.func, а лямбда, хранящаяся в self.func, имеет ссылку на self.Сборщик мусора в Python распознает циклы ссылок и очищает их , в конечном итоге , но иногда это может занять long время.В прошлом в моем коде были циклы ссылок, и эти программы часто использовали более 500 МБ памяти, потому что python не собирал ненужные объекты достаточно часто.

Правильное решение - использовать weakref модуль для создания слабой ссылки до self, например, так:

import weakref

class WeakMethod:
    def __init__(self, func, instance):
        self.func = func
        self.instance_ref = weakref.ref(instance)

        self.__wrapped__ = func  # this makes things like `inspect.signature` work

    def __call__(self, *args, **kwargs):
        instance = self.instance_ref()
        return self.func(instance, *args, **kwargs)

    def __repr__(self):
        cls_name = type(self).__name__
        return '{}({!r}, {!r})'.format(cls_name, self.func, self.instance_ref())


class FooBar(object):
    def __init__(self, func, a):
        self.a = a
        self.func = WeakMethod(func, self)

f = FooBar(foo1, 7)
print(f.func(3))  # 21

Все следующие решения создают ссылочный цикл иследовательно, плохо :

  • self.func = MethodType(func, self)
  • self.func = func.__get__(self, type(self))
  • self.func = functools.partial(func, self)
1 голос
/ 29 марта 2019

Вдохновленный этим ответом , возможное решение могло бы быть:

from types import MethodType

class FooBar1(object):
    def __init__(self, func, a):
        self.a=a
        self.func=MethodType(func, self)


def foo1(self, b):
    return self.a*b

def foo2(self, b):
    return 2*self.a*b

foobar1 = FooBar1(foo1,4)
foobar2 = FooBar1(foo2, 4)

print(foobar1.func(3))
# 12

print(foobar2.func(3))
# 24

Документация по types.MethodType , однако, не говорит о многом:

types.MethodType

Тип методов пользовательских экземпляров классов.

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