Присвоение переменной класса в качестве значения по умолчанию аргументу метода класса

Я хотел бы построить метод внутри класса с параметрами по умолчанию, взятыми из этого класса. В общем я делаю фильтрацию по некоторым данным. Внутри моего класса у меня есть метод, где обычно я передаю вектор данных. Иногда у меня нет вектора, и я беру смоделированные данные. Каждый раз, когда я не передаю конкретный вектор, я хотел бы взять смоделированные данные по умолчанию. Я думал, что это должна быть простая конструкция, где внутри моего определения метода я говорю a=self.vector, Но почему-то у меня ошибка NameError: name 'self' is not defined, Упрощенная конструкция:

class baseClass(object):  # This class takes an initial data or simulation
    def __init__(self):
        self.x = 1
        self.y = 2

class extendedClass(baseClass): # This class does some filtering
    def __init__(self):
        baseClass.__init__(self)
        self.z = 5
    def doSomething(self, a=self.z):
        self.z = 3
        self.b = a

if __name__ == '__main__':
    a = extendedClass()
    print a.__dict__
    a.doSomething()
    print a.__dict__

Результат, который я ожидал, должен быть:

{'y': 2, 'x': 1, 'z': 5}
{'y': 2, 'x': 1, 'z': 3, 'b': 5}

Я пробовал назначение по умолчанию как def doSomething(self, a=z): очевидно, это не работает никогда. Насколько я понимаю self.z виден в этой области и не должен быть проблемой, чтобы иметь его в качестве значения по умолчанию. Не знаю, почему у меня есть эта ошибка и как это сделать. Это, вероятно, простой вопрос, но я пытаюсь выяснить это или найти решение без недостатка уже некоторое время. Я нашел похожие вопросы только для других языков.

4 ответа

Решение

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

Ответ прост, по умолчанию аргумент Noneи затем проверьте это внутри метода:

def doSomething(self, a=None):
    if a is None:
        a = self.z
    self.z = 3
    self.b = a

Аргументы по умолчанию оцениваются только один раз, когда определение выполняется. Вместо этого сделайте это:

def doSomething(self, a=None):
    if a is None:
        a = self.z
    self.z = 3
    self.b = a

Смотрите также http://docs.python.org/release/3.3.0/tutorial/controlflow.html.

Вот разборка кода для простого примера модуля. Объект кода - это контейнер только для чтения для байт-кода, используемых им констант и имен, а также метаданных о количестве локальных переменных, требуемом размере стека и т. Д. Обратите внимание, что все объекты кода скомпилированы как константы. Они создаются во время компиляции. Но объекты class A а также function test создаются во время выполнения (например, когда модуль импортируется).

Чтобы сделать класс, BUILD_CLASS берет имя 'A', основания tuple(object,) и dict который содержит атрибуты пространства имен класса. Это похоже на создание экземпляра типа вручную путем вызова type(name, bases, dict), Чтобы сделать dict, функция создается из объекта кода A и позвонил. Наконец, объект класса сохраняется в пространстве имен модуля через STORE_NAME,

В объекте кода A, self.z загружается в стек в качестве аргумента MAKE_FUNCTION, Байт-код операции LOAD_NAME будет искать self в текущих локальных системах (т. е. определяемое пространство имен класса), глобальные переменные модуля и встроенные функции. Это, очевидно, потерпит неудачу, если self не определен в глобальной или встроенной области видимости; это явно не определено в местном масштабе.

Если это все-таки получилось, функция была бы создана с (self.z,) как его __defaults__ атрибут, а затем сохраняется в локальное имя test,

>>> code = compile('''
... class A(object):
...   def test(self, a=self.z): pass
... ''', '<input>', 'exec')

>>> dis.dis(code)
  2           0 LOAD_CONST               0 ('A')
              3 LOAD_NAME                0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               1 (<code object A ...>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_NAME               1 (A)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE

>>> dis.dis(code.co_consts[1]) # code object A
  2           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)

  3           6 LOAD_NAME                2 (self)
              9 LOAD_ATTR                3 (z)
             12 LOAD_CONST               0 (<code object test ...>)
             15 MAKE_FUNCTION            1
             18 STORE_NAME               4 (test)
             21 LOAD_LOCALS         
             22 RETURN_VALUE       

@uselpa: Ваш пример пастбина (переписан для 2.x):

>>> code = compile('''
... default = 1
... class Cl(object):
...     def __init__(self, a=default):
...         print a
... Cl()
... default = 2
... Cl()
... ''', '<input>', 'exec')
>>> dis.dis(code)
  2           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (default)

  3           6 LOAD_CONST               1 ('Cl')
              9 LOAD_NAME                1 (object)
             12 BUILD_TUPLE              1
             15 LOAD_CONST               2 (<code object Cl ...>)
             18 MAKE_FUNCTION            0
             21 CALL_FUNCTION            0
             24 BUILD_CLASS         
             25 STORE_NAME               2 (Cl)

  6          28 LOAD_NAME                2 (Cl)
             31 CALL_FUNCTION            0
             34 POP_TOP             

  7          35 LOAD_CONST               3 (2)
             38 STORE_NAME               0 (default)

  8          41 LOAD_NAME                2 (Cl)
             44 CALL_FUNCTION            0
             47 POP_TOP             
             48 LOAD_CONST               4 (None)
             51 RETURN_VALUE        

Как видите, объект класса Cl (и функциональный объект __init__) создается только и хранится под локальным именем 'Cl' один раз. Модуль выполняется последовательно во время выполнения, поэтому впоследствии повторное связывание имени default не будет влиять на значение по умолчанию в __init__,

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

>>> default = 1
>>> class Cl(object):
...     def __init__(self, a=default):
...         print a
... 

>>> from types import FunctionType
>>> default = 2
>>> Cl.__init__ = FunctionType(
...   Cl.__init__.__code__, globals(), '__init__', (default,), None)
>>> c = Cl()
2

Это повторно использует уже скомпилированный код объекта из __init__.__code__ создать функцию с новым __defaults__ кортеж:

>>> Cl.__init__.__defaults__
(2,)

Это будет вставить self.z если a является None/False/empty_value:

def doSomething(self, a=None):
        self.z = 3
        self.b = (a or self.z)
Другие вопросы по тегам