Есть ли способ создать экземпляр класса без вызова init?
есть ли способ обойти конструктор __init__
класса в python?
пример:
class A(object):
def __init__(self):
print "FAILURE"
def Print(self):
print "YEHAA"
теперь я хотел бы создать экземпляр A
. Это может выглядеть так, однако этот синтаксис неверен.
a = A
a.Print()
EDIT:
еще более сложный пример:
Предположим у меня есть объект C
, С какой целью он должен хранить один единственный параметр и выполнять с ним некоторые вычисления. Параметр, однако он не передается как таковой, но встроен в огромный файл параметров. Это может выглядеть примерно так:
class C(object):
def __init__(self, ParameterFile):
self._Parameter = self._ExtractParamterFile(ParameterFile)
def _ExtractParamterFile(self, ParameterFile):
#does some complex magic to extract the right parameter
return the_extracted_parameter
теперь я хотел бы сбросить и загрузить экземпляр этого объекта C
. Однако, когда я загружаю этот объект, у меня есть только одна переменная self._Parameter
и я не могу вызвать конструктор, потому что он ожидает, что файл параметров.
@staticmethod
def Load(file):
f = open(file, "rb")
oldObject = pickle.load(f)
f.close()
#somehow create newObject without calling __init__
newObject._Parameter = oldObject._Parameter
return newObject
иными словами, невозможно создать экземпляр без передачи файла параметров. В моем "реальный" случай, однако, это не файл параметров, а какой-то огромный мусор данных, который я, конечно, не хочу носить в памяти или даже хранить на диске.
и так как я хочу вернуть экземпляр C
методом Load
Я как-то должен вызвать конструктор.
СТАРОЕ РЕДАКТИРОВАНИЕ:
более сложный пример, который объясняет почему я спрашиваю:
class B(object):
def __init__(self, name, data):
self._Name = name
#do something with data, but do NOT save data in a variable
@staticmethod
def Load(self, file, newName):
f = open(file, "rb")
s = pickle.load(f)
f.close()
newS = B(???)
newS._Name = newName
return newS
Как видите, с data
не сохраняется в переменной класса я не могу передать ее в __init__
. Конечно, я мог бы просто сохранить его, но что, если данные-это огромный объект, который я не хочу постоянно носить в памяти или даже сохранять на диск?
6 ответов
вы can обойти __init__
по телефону __new__
напрямую. Затем вы можете создать объект данного типа и вызвать альтернативный метод для __init__
. Это то, что pickle
будет делать.
однако, сначала я хотел бы подчеркнуть, что это то, что вы не стоит сделать и все, что вы пытаетесь достичь, есть лучшие способы сделать это, некоторые из которых уже упоминалось в других ответов. В в частности, это плохо идея пропустить вызов __init__
.
когда объекты создаются, более или менее это происходит:
a = A.__new__(A, *args, **kwargs)
a.__init__(*args, **kwargs)
вы можете пропустить второй шаг.
вот почему не стоит этого: цель __init__
инициализировать объект, заполнить все поля и убедиться, что __init__
также вызываются методы родительских классов. С pickle
это исключение, потому что он пытается для хранения всех данных, связанных с объектом (включая любой поля/переменные, которые заданы для объекта), и поэтому все, что было установлено __init__
Предыдущее время будет восстановлено рассолом, нет необходимости вызывать его снова.
если вы пропустите __init__
и используйте альтернативный инициализатор, у вас будет своего рода дублирование кода - будет два места, где заполняются переменные экземпляра, и легко пропустить один из них в одном из инициализаторы или случайно заставляют два заполнения полей действовать по-разному. Это дает возможность тонких ошибок, которые не так тривиальны для отслеживания (вам придется знаю который инициализатор был вызван), и код будет сложнее поддерживать. Не говоря уже о том, что вы будете в еще большем беспорядке, если используете наследование - проблемы будут идти вверх по цепочке наследования, потому что вам придется использовать этот альтернативный инициализатор повсюду цепь.
также, сделав это, вы будете более или менее переопределять создание экземпляра Python и создавать свой собственный. Python уже делает это для вас довольно хорошо, не нужно изобретать его, и это запутает людей, использующих ваш код.
вот что лучше всего сделать вместо этого: использовать один __init__
метод, который должен вызываться для всех возможных экземпляров класса, который инициализирует все переменные экземпляра должным образом. Для различных режимов использования инициализации любой из двух подходов:--21-->
- поддержка различных подписей для
__init__
которые обрабатывают ваши случаи, используя необязательные аргументы. - Создайте несколько методов класса, которые служат альтернативными конструкторами. Убедитесь, что все они создают экземпляры класса обычным способом (т. е. вызывая
__init__
), как показал Роман Боднарчук, при выполнении дополнительной работы или. Лучше всего, если они передадут все данные классу (и__init__
обрабатывает его), но если это невозможно или неудобно, вы можете установить некоторые переменные экземпляра после создания экземпляра и__init__
делается инициализация.
если __init__
имеет необязательный шаг (например, как обработка этого data
аргумент, хотя вам нужно быть более конкретным), вы можете сделать его необязательным аргументом или сделать обычный метод, который выполняет обработку... или обоих.
использовать classmethod
декоратор для вашего Load
способ:
class B(object):
def __init__(self, name, data):
self._Name = name
#store data
@classmethod
def Load(cls, file, newName):
f = open(file, "rb")
s = pickle.load(f)
f.close()
return cls(newName, s)
так что вы можете сделать:
loaded_obj = B.Load('filename.txt', 'foo')
Edit:
в любом случае, если вы все же хотите опустить __init__
способ, попробовать __new__
:
>>> class A(object):
... def __init__(self):
... print '__init__'
...
>>> A()
__init__
<__main__.A object at 0x800f1f710>
>>> a = A.__new__(A)
>>> a
<__main__.A object at 0x800f1fd50>
принимая ваш вопрос буквально, я бы использовал мета-классы:
class MetaSkipInit(type):
def __call__(cls):
return cls.__new__(cls)
class B(object):
__metaclass__ = MetaSkipInit
def __init__(self):
print "FAILURE"
def Print(self):
print "YEHAA"
b = B()
b.Print()
Это может быть полезно, например, для копирования конструкторов без загрязнения списка параметров. Но сделать это правильно было бы больше работы и заботы, чем мой предлагаемый Хак.
не совсем так. Цель __init__
- это создание экземпляра объекта, и по умолчанию он действительно ничего не делает. Если __init__
метод не делает то, что вы хотите, и это не ваш собственный код для изменения, вы можете выбрать, чтобы переключить его. Например, принимая ваш класс A, мы могли бы сделать следующее, Чтобы избежать вызова этого __init__
способ:
def emptyinit(self):
pass
A.__init__ = emptyinit
a = A()
a.Print()
это будет динамически переключаться, который __init__
метод из класса, заменив его пустым вызовом. Заметить что это, вероятно, не очень хорошо делать, так как он не называет супер-класс __init__
метод.
вы также могли бы расширить его, чтобы создать свой собственный класс, который делает все то же самое, за исключением переопределения __init__
метод, чтобы делать то, что вы хотите (возможно, ничего).
возможно, однако, вы просто хотите вызвать метод класса без создания экземпляра объекта. Если это так, вы должны посмотреть в @classmethod и @staticmethod оформителей. Они допускают именно такой тип поведения.
в вашем коде вы поставили декоратор @staticmethod, который не принимает
Я читал поваренную книгу Python, и есть раздел, говорящий об этом: пример приведен с использованием __new__
в обход __init__()
>>> class A: def __init__(self,a): self.a = a >>> test = A('a') >>> test.a 'a' >>> test_noinit = A.__new__(A) >>> test_noinit.a Traceback (most recent call last): File "", line 1, in test_noinit.a AttributeError: 'A' object has no attribute 'a' >>>
однако я думаю, что это работает только в Python3. Ниже работает под 2.7
>>> class A: def __init__(self,a): self.a = a >>> test = A.__new__(A) Traceback (most recent call last): File "", line 1, in test = A.__new__(A) AttributeError: class A has no attribute '__new__' >>>
как я сказал в своем комментарии, Вы можете изменить свой __init__
метод, позволяющий создавать без указания каких-либо значений его параметров:
def __init__(self, p0, p1, p2):
# some logic
станет:
def __init__(self, p0=None, p1=None, p2=None):
if p0 and p1 and p2:
# some logic
или:
def __init__(self, p0=None, p1=None, p2=None, init=True):
if init:
# some logic