Есть ли способ создать экземпляр класса без вызова 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-->

  1. поддержка различных подписей для __init__ которые обрабатывают ваши случаи, используя необязательные аргументы.
  2. Создайте несколько методов класса, которые служат альтернативными конструкторами. Убедитесь, что все они создают экземпляры класса обычным способом (т. е. вызывая __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