Присвоение переменной класса в качестве значения по умолчанию аргументу метода класса
Я хотел бы создать метод внутри класса со значениями по умолчанию, взятыми из этого класса. В общем, я фильтрую некоторые данные. Внутри моего класса у меня есть метод, где обычно передаю вектор данных. Иногда у меня нет вектора, и я беру смоделированные данные. Каждый раз, когда я не передаю конкретный вектор, я бы хотел поместить имитированные данные по умолчанию. Я думал, что это должна быть легкая конструкция, где внутри моего определения метода я говорю 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
виден в этой области и не должен быть проблемой, чтобы иметь его как значение по умолчанию. Не знаю, почему у меня есть эта ошибка и как это сделать. Это, наверное, простой вопрос, но я пытаюсь понять это или найти решение без каких-либо недостатков. Я нашел похожие вопросы только для других языков.
Ответы
Ответ 1
Ваше понимание неверно. self
сам по себе является параметром определения этой функции, поэтому он не может быть в области видимости в этой точке. Это только в области видимости внутри самой функции.
Ответ заключается в том, чтобы по умолчанию аргумент None
, а затем проверить его внутри метода:
def doSomething(self, a=None):
if a is None:
a = self.z
self.z = 3
self.b = a
Ответ 2
Здесь разбор кода для простого примерного модуля. Объект кода является контейнером только для чтения для байт-кода, константами и именами, которые он использует, и метаданными о количестве локальных переменных, требуемом размере стека и т.д. Обратите внимание, что все объекты кода скомпилированы как константы. Они создаются во время компиляции. Но объекты class A
и function test
создаются во время выполнения (например, когда модуль импортируется).
Чтобы сделать класс, BUILD_CLASS
берет имя 'A'
, базы tuple
(object,)
и dict
, которые содержат атрибуты пространства имен классов. Это похоже на создание экземпляра типа вручную, вызывая type(name, bases, dict)
. Чтобы сделать dict
, функция создается из объекта кода A
и вызывается. Наконец, объект класса хранится в пространстве имен модулей через STORE_NAME
.
В объекте кода A
, self.z
загружается в стек как аргумент MAKE_FUNCTION
. Байт-код op 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: Пример вашего pastebin (переписан для 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,)
Ответ 3
Аргументы по умолчанию получают оценку только один раз, когда выполняется определение. Вместо этого сделайте следующее:
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#more-on-defining-functions.
Ответ 4
Это вставит self.z
если a
None
/False
/empty_value
:
def doSomething(self, a=None):
self.z = 3
self.b = (a or self.z)