2014-02-07 2 views
0

, чтобы автоматически генерировать параметризованные тесты, я пытаюсь добавить методы в класс путем замораживания некоторых параметров существующего метода. Вот фрагмент кода Python 3С помощью setattr, чтобы заморозить некоторые параметры метода

class A: 
    def f(self, n): 
     print(n) 

params = range(10) 

for i in params: 
    name = 'f{0}'.format(i) 
    method = lambda self: A.f(self, i) 
    setattr(A, name, method) 

Однако, следующие строки, а затем производят Неудовлетворительно OUTPUT

a = A() 
a.f0() 

отпечатки "9" (вместо "0"). Я должен что-то делать неправильно, но я не вижу, что. Вы можете помочь ?

Большое спасибо


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

+1

для таких вещей в тестах try [mock] (http://www.voidspace.org.uk/python/mock/) – warvariuc

+1

Возможный дубликат [Генерирующие функции внутри цикла с выражением лямбда в python] (http: // stackoverflow.com/questions/1841268/generating-functions-inside-loop-with-lambda-expression-in-python) – delnan

+0

@warwaruk: посмотрев на краткое руководство по началу работы, я не уверен, как макет может помочь ... но Я очень рад узнать об этом пакете. Может оказаться полезным в какой-то момент. – Sebastien

ответ

1

Попробуйте

method = lambda self, i=i: A.f(self, i) 

, потому что в противном случае при вызове метода значение i «s может быть изменен

+0

Связано ли это с тем, что переменные Python каким-то образом отличаются от, например, Ссылки на Java? Где я могу узнать больше об этих тонкостях? – Sebastien

+1

Это связано с переменными областями. 'lambda' - простая функция с телом' A.f (self, i) '. Когда вы вызываете метод с этим телом, запрашивается значение «i». Он берется из внешнего пространства. Поиск SO и google для закрытий – warvariuc

+1

@Sebastien Это связано с тем, что без '' i '' значение 'i' не будет определено до тех пор, пока функция лямбда не будет выполнена (в этом случае она будет использоваться с последними значение, присвоенное 'i', которое после вашего цикла будет' 9'). Это верно для всех функций Python, и по этой причине вы можете иметь проблемы при обращении к нелокальным переменным (но это также причина, по которой вы можете объявить функцию перед объявлением нелокального значения, которое она использует). В Python есть немного больше возможностей, чем это, но в этом все работает. – JAB

1

Лучший способ„заморозить“параметры в Python является использование functools.partial. Это примерно эквивалентно лямбда-версии warwaruk, но если у вас есть функция с множеством аргументов, но только хотите заморозить один или два из них (или если вы знаете только определенные аргументы и не заботитесь обо всем остальном), используя partial, это более элегантно поскольку вы указываете только аргументы, которые хотите заморозить, вместо того, чтобы повторять целую подпись функции в лямбда.

Пример для вашей программы:

class A: 
    def f(self, n): 
     print(n) 

from functools import partial 

for i in range(10): # params 
    setattr(A, 'f{0}'.format(i), partial(A.f, n=i)) 

В зависимости от версии Python 3 вы используете, вам не нужно включать 0 в формате строки заполнителем; начиная с 3.1, iirc, он должен быть автоматически заменен.