2016-03-24 5 views
1

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

Это функция, которая делает функции вызываемыми без выполнения при создании моей кнопки. Обычно он отлично работает, но с некоторыми функциями он случайно отклоняет любой ввод! Вот код:

class Callable(object): 
    def __init__(self, func, *args, **kwds): 
     self.func = func 
     self.args = args 
     self.kwds = kwds 

    def __call__(self, *args, **kwds): 
     return self.func(self.args) 

    def __str__(self): 
     return self.func.__name 

Кажется совершенно случайным, какие вопросы принимаются, а какие нет. Я действительно отчаянный, потому что для написания своего синонима этого класса требуется много времени, я адаптирую их с числом args и kwds, тогда он работает нормально. Но теперь я подошел к тому моменту, когда не знаю, сколько аргументов я собираюсь пройти, так что это больше не сработает.

Вопрос:

  • Почему этот класс не принимает все функции?
  • Как я могу изменить это поведение?
+0

Приведите пример функции, которая работает, и функцию, которая этого не делает. – Kevin

+0

@Kevin: Это будет сложно, я использую модуль под названием болотный, это модуляция для tkinter. Он меняет весь синтаксис, и, как я уже сказал, это случайные функции, которые работают, а какие нет. Я совершенно уверен, что видел бы образец –

+0

Можете ли вы объяснить, что именно это должно делать? например вы, кажется, передаете '* args' как' __init__', так и '__call__'. Если бы я сделал: 'Callable (func, 1, 2, 3) (4, 5)' - Каков эквивалентный вызов 'func', который вы ожидаете? например (1, 2, 3)), 'func (1, 2, 3, 4, 5)', 'func (4, 5)) ', ... – mgilson

ответ

0

Ваш класс используется для предоставления дополнительного контекста обратным вызовам tkinter, поэтому обратный вызов может действительно сделать что-то полезное. Вы почти правы, за исключением того, что вам нужно распаковать исходные аргументы и kwds при вызове функции. Кроме того, не включайте никаких аргументов в __call__, потому что вы их не принимаете.

class Callable(object): 
    def __init__(self, func, *args, **kwds): 
     self.func = func 
     self.args = args 
     self.kwds = kwds 

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

    def __str__(self): 
     return self.func.__name 


def some_callback(a, b, c=None): 
    print(a,b,c) 

Button(text="Do not press", 
    command=Callable(some_callback, 1, 2, 3)) 

Это также может быть сделано без класса, используя вместо лямбды

def some_callback(a, b, c=None): 
    print(a,b,c) 

Button(text="Do not press", 
    command=lambda: some_callback(1, 2, 3)) 
+0

Эта вещь лямбда работала отлично, спасибо вам большое! –

3

Я считаю, что это то, что вы ищете:

class Callable(object): 
    def __init__(self, func, *args, **kwds): 
     self.func = func 
     self.args = args 
     self.kwds = kwds 

    def __call__(self, *args, **kwds): 
     return self.func(*self.args, *args, **self.kwds, **kwds) 

    def __str__(self): 
     return self.func.__name 

Вы должны распаковать его с помощью оператора * и ** для ключевых аргументов. Таким образом вы передаете переменные и переменные вызова функции.

UPDATE:

Для питона версий старше 3.5, это будет работать:

class Callable(object): 
    def __init__(self, func, *args, **kwds): 
     self.func = func 
     self.args = args 
     self.kwds = kwds 

    def __call__(self, *args, **kwds): 
     args = self.args + args 
     kwargs = kwds.copy() 
     kwargs.update(self.kwds) 
     return self.func(*args, **kwargs) 

    def __str__(self): 
     return self.func.__name 

Используя это решение, сначала дать переменные приобретенные __init__ затем переменные, передаваемые в __call__.

+0

Примечание. Этот синтаксис с операторами mutiple splat недоступен до [python3.5] (https://www.python.org/dev/peps/pep-0448/) IIRC. Для более ранних версий вам понадобится '* (self.args + args)' и сделать аналогичную вещь для распаковки аргументов ключевого слова ... 'kwds.update (self .kwds); func (* (self.args + args), ** kwds) ' – mgilson

+0

Извините, я python 3.4. Можете ли вы упаковать его в свой ответ, пожалуйста? Я не уверен, что делать сейчас! –

+1

Этот ответ накладывает нечетное требование на позиционные аргументы. Рассмотрим 'fctn (a, b, c, d)' - если любые аргументы заданы инициализатору класса, они будут предшествовать любым аргументам, переданным '__call__'. Если OP пытается реализовать частичный класс, то, возможно, это хорошо, но это должно быть четко указано. – tdelaney

0

Пожалуйста, обратите внимание, используя следующий класс. Он позволяет указывать позиционные аргументы и аргументы ключевых слов во время создания экземпляра или вызова экземпляра (создание нового экземпляра или вызов его). Поскольку это двусмысленно, что подразумевалось бы, если бы оба типа аргументов указывались в оба раза, класс отказывается догадываться, какой порядок или приоритеты были предназначены, и создает RuntimeError для предотвращения неопределенного поведения. Кроме того, метод __str__ должен по-прежнему работать, если ваша функция или другой вызываемый объект не имеет атрибута __name__.

class Callable: 

    def __init__(self, function, *args, **kwargs): 
     self.__function, self.__args, self.__kwargs = function, args, kwargs 

    def __call__(self, *args, **kwargs): 
     if (args or kwargs) and (self.__args or self.__kwargs): 
      raise RuntimeError('multiple args and kwargs are not supported') 
     if args or kwargs: 
      return self.__function(*args, **kwargs) 
     return self.__function(*self.__args, **self.__kwargs) 

    def __str__(self): 
     return getattr(self.__function, '__name__', 'function') 

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

Смежные вопросы