2015-04-24 4 views
2

Я написал класс как это:__enter __() занимает ровно 3 аргумента (1 дал)

class FooBar(object): 
    # some methods 
    # ... 
    def __enter__(self, param1, param2): 
     # do something here ... 
     pass 

Я пытаюсь использовать мой класс, как это (импортирован из модуля MyMod):

with (mymod.FooBar("hello", 123)) as x: 
    # do something here with instance of mymod.FooBar called x ... 
    pass 

, когда блок выше запускается на выполнение, я получаю ошибку:

__enter__() takes exactly 3 arguments (1 given) 

Что я делаю неправильно?

ответ

11

Метод __enter__ никогда не дает никаких аргументов, поэтому за пределами self ваша подпись не должна иметь никаких других.

Вы должны переместить эти аргументы __init__ методы вместо:

class FooBar(object): 
    def __init__(self, param1, param2): 
     # do something here ... 

    def __enter__(self): 
     # something else, perhaps return self 

Создание экземпляра FooBar() является отдельным этапом. with вызывает __enter__ на результат вашего выражения mymod.FooBar("hello", 123), само выражение не переведено на звонок __enter__.

Если бы это было, вы не могли использовать его, как это, но вы можете:

cm = mymod.FooBar("hello", 123) 
with cm as x: 
    # do something here with x, which is the return value of cm.__enter__() 

Обратите внимание, что x назначается независимо cm.__enter__() возвращается; вы можете вернуть self от __enter__ или вы можете вернуть что-то совершенно другое.

Ожидаемые методы __enter__ и __exit__ задокументированы в With Statement Context Managers section модельной документации Python данных:

object.__enter__(self)

Enter the runtime context related to this object. The with statement will bind this method’s return value to the target(s) specified in the as clause of the statement, if any.

, а также в Content Manager Types section из встроенных типов документации:

contextmanager.__enter__()

Enter the runtime context and return either this object or another object related to the runtime context. The value returned by this method is bound to the identifier in the as clause of with statements using this context manager.

An example of a context manager that returns itself is a file object. File objects return themselves from __enter__() to allow open() to be used as the context expression in a with statement.

Если вас интересуют точные взаимодействия, см. Первоначальное предложение: PEP 343 -- The "with" Statement; из раздела спецификации вы можете увидеть, что with EXPR as VAR: BLOCK заявления делает под капотом:

mgr = (EXPR) 
exit = type(mgr).__exit__ # Not calling it yet 
value = type(mgr).__enter__(mgr) 
exc = True 
try: 
    try: 
     VAR = value # Only if "as VAR" is present 
     BLOCK 
    except: 
     # The exceptional case is handled here 
     exc = False 
     if not exit(mgr, *sys.exc_info()): 
      raise 
     # The exception is swallowed if exit() returns true 
finally: 
    # The normal and non-local-goto cases are handled here 
    if exc: 
     exit(mgr, None, None, None) 

Обратите внимание на mgr = (EXPR) части; в вашем случае, mymod.FooBar("hello", 123) - эта часть. Также обратите внимание, что (EXPR), __enter__ и __exit__ здесь не защищены 'try..except, исключения, поднятые в выражении или при входе или выходе, равны , а не, обработанный менеджером контекста!

+0

Но мне нужно передать params в ctor (constructor), чтобы создать допустимый объект –

+4

@HomunculusReticulli: yes, но '__enter__' is * не является конструктором *. –

+0

Учитывая, ваш очень высокий балл, могу ли я быть смелым, чтобы спросить вас, уверены ли вы в этом. Я всегда думал (читал где-нибудь?), Что оператор with ** создает экземпляр ** объекта (в других словах это был просто синтаксический сахар для ctor), аналогично __exit__ был аналогичен dtor. Казалось бы, большие участки моего кода ошибочны, если это так. –

0

mymod.FooBar() вызывает метод __init__ класса FooBar и возвращает экземпляр класса в качестве объекта.

Ваш метод __enter__ должен принимать только self

2

Рассмотрим этот код:

f = open("myfile.txt") 
with f: 
    x = f.read() 

Это в основном так же, как

with open("myfile.txt") as f: 
    x = f.read() 

Обратите внимание, как инициализация объекта и контекста отдельные вещи , Контекст с только отвечает за вызов объекта введите() и выход() методы в соответствующее время.

+0

+1 А как-то я, кажется, понимаю это, теперь, когда вы показываете, как два блока семантически эквивалентны ... Моя уверенность, однако, сильно потрясена: (Кажется, я не знал столько Python, как я думал, что сделал ... –

+1

@HomunculusReticulli: они не * вполне * эквивалентны, но для файловых объектов это не имеет большого значения. В первом примере вы можете делать 'f.close()' между ними, и тогда вы получите исключение при использовании 'with f:', например. Но 'f .__ enter __()' возвращает 'f', так что кроме этого два фрагмента в основном эквивалентны. –

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