Should all member variables be initialized in __init__

前端 未结 3 1130
独厮守ぢ
独厮守ぢ 2020-12-08 02:05

Maybe this is more of a style question than a technical one but I have a class with several member variables and I want to have it work so that some of the member variables

相关标签:
3条回答
  • 2020-12-08 02:28

    In object-oriented programming it's up to the developer to ensure an object is always in a consistent state after instantiation and after a method finishes. Other than that you're free to develop the class as you wish (keeping in mind certain principles with subclassing / overriding and so on).

    A tool such as Pylint will warn when you're setting instance variables outside __init__. It can be argued that setting all instance variables in the __init__ is cleaner but it's not a rule that must be abided by at all times.

    0 讨论(0)
  • 2020-12-08 02:38

    I would actually discourage initializing variables you don't always need in __init__ to an arbitrary default value.

    I do question your use of OO if this is the case, but I'm sure there is a valid and understandable case where __init__ will not do everything, and the class will want to further modify itself by adding additional attributes with other methods.

    The proper way in my opinion to test if a variable was set while running a method that may want to use it would be to use hasattr. This is in the case that this is a valid way to use the method and the test just switches behavior in a sensible way.

    Another way would be to try and use it and handle the exception and provide some user friendly information about what the user of your class is doing wrong. This is in the case the method needs the attribute to be set before running.

    i.e. Hey man, you did initialize the class, but you need to make sure the z attribute exists by calling the z_init method before running the z_run method.

    Another, arguably the more pythonic way, would be to just document how to use the method in the docstring and then let the exception fly when it is used improperly. This is good enough for the first implementation of something and you can then focus on the next task. This is in the same situation as above, the method needs the attribute to be set.

    The reason I do not like the idea of initializing variables to arbitrary defaults is this can be confusing (because it is arbitrary) and is line noise.

    If the value is not arbitrary and simply a default value that can be changed you should be using a default value in the __init__ method that can be overridden. It can also actually be a valid initial state, which is also not arbitrary and you should set it in the __init__ method.

    So the real answer is it depends, and you should probably avoid it and question your use of OO if you are doing this either by adding attributes in other methods or initializing attributes to arbitrary values.

    While Simeon Visser is saying to keep your object in a consistent state, he has no basis for what consistency is based on your abstract example. While Pylint warns on this kind of thing, warnings from lint programs are simply so a high level reviewer can be alerted of things that usually indicate code smell. I say high level reviewer because a real reviewer should be reading and understanding all of your code, and thus not really need Pylint.

    An example that breaks the rule of thumb:

    class Mutant(object):
        """A mutant!"""
    
        def __init__(self):
            """A mutant is born with only 1 eye and 1 mouth"""
    
            self.eyes = 1
            self.mouth = 1
            self.location = 'Montana'
    
        def roll_to(self, location):
            """If they have limbs, running is less dangerous"""
    
            if hasattr(self, 'limbs'):
                 print 'Your mutant broke its limbs off!!'
                 del self.limbs
    
            self.location = location
    
        def run_to(self, location):
            """If they don't have limbs, running is not effective"""
    
            if not hasattr(self, 'limbs'):
                 print 'Your mutant tries to run but he has no limbs.'
            else:
                 self.location = location
    
        def grow_limbs(self, number_of_limbs):
             """Ah, evolution!"""
    
             assert number_of_limbs > 0, 'Cannot grow 0 or less limbs...'
    
             if hasattr(self, 'limbs'):
                 self.limbs += number_of_limbs
             else:
                 self.limbs = number_of_limbs
    
    0 讨论(0)
  • 2020-12-08 02:51

    Here is an excerpt from sololearn.com (a free site to learn python)

    "Properties provide a way of customizing access to instance attributes. They are created by putting the property decorator above a method, which means when the instance attribute with the same name as the method is accessed, the method will be called instead.

    One common use of a property is to make an attribute read-only."

    Example (also from sololearn.com):

    class Pizza:
        def __init__(self, toppings):
        self.toppings = toppings
    
        @property
        def pineapple_allowed(self):
           return False
    
       pizza = Pizza(["cheese", "tomato"])
       print(pizza.pineapple_allowed)
       pizza.pineapple_allowed = True
    

    Result:

      >>>
     False
     AttributeError: can't set attribute
     >>>
    

    If var3 depends on var1 and var2 you could do

    class myClass:
        def __init__(self,var1,var2):
            self.var1=var1
            self.var2=var2
        @property
        def var3(self):
            return(self.var1+self.var2)  #var3 depends on var1 and var2
     m1=myClass(1,2)
     print(m1.var3)   # var3 is 3
    

    var3 can also be set to whatever you want using a setter function. Note that you can avoid setting var3 to an arbitrary value by using None.

    class myClass2(object):
        def __init__(self,var1,var2):
            self.var1=var1
            self.var2=var2
            self._var3=None     # None or an initial value that makes sense
            @property
            def var3(self):
                return(self._var3)
            @var3.setter
            def var3(self,value):
                self._var3=value
       m2=myClass(1,2)
       print(m2.var3)        # var3 is none
       print(m2.var3(10))    # var3 is set to 10 
    
    0 讨论(0)
提交回复
热议问题