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

OLD EDIT:

Более сложный пример, объясняющий почему. Я задаю вопрос:

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__. Конечно, я мог бы просто сохранить его, но что, если данные являются огромным объектом, который я не хочу постоянно носить в памяти или даже сохранять на диске?

Ответ 1

Вы можете обойти __init__, позвонив __new__ напрямую. Затем вы можете создать объект данного типа и вызвать альтернативный метод для __init__. Это то, что pickle будет делать.

Однако, во-первых, я бы очень хотел подчеркнуть, что это то, что вы не должны делать, и что бы вы ни пытались достичь, есть лучшие способы сделать это, некоторые из которых были упомянуты в других ответах. В частности, не рекомендуется пропускать вызов __init__.

Когда объекты создаются, более или менее это происходит:

a = A.__new__(A, *args, **kwargs)
a.__init__(*args, **kwargs)

Вы можете пропустить второй шаг.

Вот почему вы не должны этого делать: Цель __init__ - инициализировать объект, заполнить все поля и убедиться, что методы __init__ родительских классов также вызываются. С pickle это исключение, потому что он пытается сохранить все данные, связанные с объектом (включая любые поля/переменные экземпляра, которые установлены для объекта), и поэтому все, что было установлено __init__ в предыдущий раз, будет восстановлено pickle, нет необходимости звонить снова.

Если вы пропустите __init__ и воспользуетесь альтернативным инициализатором, у вас будет своего рода дублирование кода - в двух местах будут заполнены переменные экземпляра, и будет легко пропустить одно из них в одном из инициализаторов или случайно сделать два поля заполняют, действуют по-разному. Это дает возможность скрытых ошибок, которые не так просто отследить (вам нужно знать, какой инициализатор был вызван), и код будет сложнее поддерживать. Не говоря уже о том, что вы будете в еще большем беспорядке, если будете использовать наследование - проблемы будут идти вверх по цепочке наследования, потому что вам придется использовать этот альтернативный инициализатор везде по цепочке.

Кроме того, вы будете более или менее переопределять создание экземпляров Python и создавать свои собственные. Python уже делает это для вас довольно хорошо, не нужно изобретать его заново, и это может смутить людей, использующих ваш код.

Вот что лучше всего сделать вместо этого: Используйте один метод __init__ который должен вызываться для всех возможных экземпляров класса, который правильно инициализирует все переменные экземпляра. Для разных режимов инициализации используйте любой из двух подходов:

  1. Поддерживайте различные подписи для __init__ которые обрабатывают ваши случаи, используя необязательные аргументы.
  2. Создайте несколько методов класса, которые служат альтернативными конструкторами. Убедитесь, что все они создают экземпляры класса обычным способом (то есть, вызывая __init__), как показано Романом Боднарчуком, при выполнении дополнительной работы или чего-то еще. Лучше всего, если они передадут все данные классу (и __init__ обрабатывает их), но если это невозможно или неудобно, вы можете установить некоторые переменные экземпляра после того, как экземпляр был создан, и __init__ инициализацию.

Если __init__ имеет необязательный шаг (например, обработку этого аргумента data, хотя вам нужно быть более конкретным), вы можете либо сделать его необязательным аргументом, либо создать обычный метод, который выполняет обработку... или оба.

Ответ 2

Используйте classmethod decorator для вашего метода 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>

Ответ 3

Взяв ваш вопрос в буквальном смысле, я бы использовал мета-классы:

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()

Это может быть полезно, например, для копирования конструкторов без загрязнения списка параметров. Но для этого было бы лучше работать и заботиться, чем предлагаемый вами взлом.

Ответ 4

Не совсем. Цель __init__ - создать экземпляр объекта, и по умолчанию он действительно ничего не делает. Если метод __init__ не делает то, что вы хотите, и это не значит, что ваш собственный код изменится, вы можете его отключить. Например, взяв ваш класс A, мы могли бы сделать следующее, чтобы избежать вызова этого метода __init__:

def emptyinit(self):
    pass
A.__init__ = emptyinit
a = A()
a.Print()

Это будет динамически отключать метод __init__ из класса, заменяя его пустым вызовом. Обратите внимание, что это, вероятно, НЕ ДОЛЖНО делать, поскольку он не вызывает метод супер класса __init__.

Вы также можете подклассифицировать его, чтобы создать свой собственный класс, который делает все то же самое, кроме переопределения метода __init__, чтобы делать то, что вы хотите (возможно, ничего).

Возможно, однако, вы просто хотите вызвать метод из класса без создания экземпляра объекта. Если это так, вы должны изучить @classmethod и @staticmethod декораторы. Они допускают именно такой тип поведения.

В вашем коде вы поместили декоратор @staticmethod, который не принимает аргумент self. Возможно, для этой цели может быть лучше, чем @classmethod, который может выглядеть примерно так:

@classmethod
def Load(cls, file, newName):
    # Get the data
    data = getdata()
    # Create an instance of B with the data
    return cls.B(newName, data)

ОБНОВЛЕНИЕ: Рош Отличный ответ указал, что вы не можете позвонить __init__, внедрив __new__, о котором я действительно не знал (хотя это имеет смысл). Спасибо Рош!

Ответ 5

Я читал поваренную книгу 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__'
>>> 

Ответ 6

Как я уже сказал в своем комментарии, вы можете изменить свой метод __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