Is there a way to instantiate a class without calling __init__?

前端 未结 6 1143
故里飘歌
故里飘歌 2020-11-28 05:04

Is there a way to circumvent the constructor __init__ of a class in python?

Example:

class A(object):    
    def __init__(self):
               


        
相关标签:
6条回答
  • 2020-11-28 05:35

    You can circumvent __init__ by calling __new__ directly. Then you can create a object of the given type and call an alternative method for __init__. This is something that pickle would do.

    However, first I'd like to stress very much that it is something that you shouldn't do and whatever you're trying to achieve, there are better ways to do it, some of which have been mentioned in the other answers. In particular, it's a bad idea to skip calling __init__.

    When objects are created, more or less this happens:

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

    You could skip the second step.

    Here's why you shouldn't do this: The purpose of __init__ is to initialize the object, fill in all the fields and ensure that the __init__ methods of the parent classes are also called. With pickle it is an exception because it tries to store all the data associated with the object (including any fields/instance variables that are set for the object), and so anything that was set by __init__ the previous time would be restored by pickle, there's no need to call it again.

    If you skip __init__ and use an alternative initializer, you'd have a sort of a code duplication - there would be two places where the instance variables are filled in, and it's easy to miss one of them in one of the initializers or accidentally make the two fill the fields act differently. This gives the possibility of subtle bugs that aren't that trivial to trace (you'd have to know which initializer was called), and the code will be more difficult to maintain. Not to mention that you'd be in an even bigger mess if you're using inheritance - the problems will go up the inheritance chain, because you'd have to use this alternative initializer everywhere up the chain.

    Also by doing so you'd be more or less overriding Python's instance creation and making your own. Python already does that for you pretty well, no need to go reinventing it and it will confuse people using your code.

    Here's what to best do instead: Use a single __init__ method that is to be called for all possible instantiations of the class that initializes all instance variables properly. For different modes of initialization use either of the two approaches:

    1. Support different signatures for __init__ that handle your cases by using optional arguments.
    2. Create several class methods that serve as alternative constructors. Make sure they all create instances of the class in the normal way (i.e. calling __init__), as shown by Roman Bodnarchuk, while performing additional work or whatever. It's best if they pass all the data to the class (and __init__ handles it), but if that's impossible or inconvenient, you can set some instance variables after the instance was created and __init__ is done initializing.

    If __init__ has an optional step (e.g. like processing that data argument, although you'd have to be more specific), you can either make it an optional argument or make a normal method that does the processing... or both.

    0 讨论(0)
  • 2020-11-28 05:41

    I was reading the Python cookbook and there's a section talking about this: the example is given using __new__ to bypass __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'
    >>> 
    

    However I think this only works in Python3. Below is running under 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__'
    >>> 
    
    0 讨论(0)
  • 2020-11-28 05:49

    As I said in my comment you could change your __init__ method so that it allows creation without giving any values to its parameters:

    def __init__(self, p0, p1, p2):
       # some logic
    

    would become:

    def __init__(self, p0=None, p1=None, p2=None):
       if p0 and p1 and p2:
           # some logic
    

    or:

    def __init__(self, p0=None, p1=None, p2=None, init=True):
        if init:
            # some logic
    
    0 讨论(0)
  • 2020-11-28 05:52

    Use classmethod decorator for your Load method:

    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)
    

    So you can do:

    loaded_obj = B.Load('filename.txt', 'foo')
    

    Edit:

    Anyway, if you still want to omit __init__ method, try __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>
    
    0 讨论(0)
  • 2020-11-28 05:53

    Taking your question literally I would use meta classes :

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

    This can be useful e.g. for copying constructors without polluting the parameter list. But to do this properly would be more work and care than my proposed hack.

    0 讨论(0)
  • 2020-11-28 05:57

    Not really. The purpose of __init__ is to instantiate an object, and by default it really doesn't do anything. If the __init__ method is not doing what you want, and it's not your own code to change, you can choose to switch it out though. For example, taking your class A, we could do the following to avoid calling that __init__ method:

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

    This will dynamically switch out which __init__ method from the class, replacing it with an empty call. Note that this is probably NOT a good thing to do, as it does not call the super class's __init__ method.

    You could also subclass it to create your own class that does everything the same, except overriding the __init__ method to do what you want it to (perhaps nothing).

    Perhaps, however, you simply wish to call the method from the class without instantiating an object. If that is the case, you should look into the @classmethod and @staticmethod decorators. They allow for just that type of behavior.

    In your code you have put the @staticmethod decorator, which does not take a self argument. Perhaps what may be better for the purpose would a @classmethod, which might look more like this:

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

    UPDATE: Rosh's Excellent answer pointed out that you CAN avoid calling __init__ by implementing __new__, which I was actually unaware of (although it makes perfect sense). Thanks Rosh!

    0 讨论(0)
提交回复
热议问题