How do I avoid the “self.x = x; self.y = y; self.z = z” pattern in __init__?

后端 未结 11 1470
醉梦人生
醉梦人生 2020-12-12 11:16

I see patterns like

def __init__(self, x, y, z):
    ...
    self.x = x
    self.y = y
    self.z = z
    ...

quite frequently, often with

相关标签:
11条回答
  • 2020-12-12 11:18

    You could also do:

    locs = locals()
    for arg in inspect.getargspec(self.__init__)[0][1:]:
        setattr(self, arg, locs[arg])
    

    Of course, you would have to import the inspect module.

    0 讨论(0)
  • 2020-12-12 11:23

    explicit is better than implicit ... so sure you could make it more concise:

    def __init__(self,a,b,c):
        for k,v in locals().items():
            if k != "self":
                 setattr(self,k,v)
    

    The better question is should you?

    ... that said if you want a named tuple I would recommend using a namedtuple (remember tuples have certain conditions attached to them) ... perhaps you want an ordereddict or even just a dict ...

    0 讨论(0)
  • 2020-12-12 11:30

    To expand on gruszczys answer, I have used a pattern like:

    class X:
        x = None
        y = None
        z = None
        def __init__(self, **kwargs):
            for (k, v) in kwargs.items():
                if hasattr(self, k):
                    setattr(self, k, v)
                else:
                    raise TypeError('Unknown keyword argument: {:s}'.format(k))
    

    I like this method because it:

    • avoids repetition
    • is resistant against typos when constructing an object
    • works well with subclassing (can just super().__init(...))
    • allows for documentation of the attributes on a class-level (where they belong) rather than in X.__init__

    Prior to Python 3.6, this gives no control over the order in which the attributes are set, which could be a problem if some attributes are properties with setters that access other attributes.

    It could probably be improved upon a bit, but I'm the only user of my own code so I am not worried about any form of input sanitation. Perhaps an AttributeError would be more appropriate.

    0 讨论(0)
  • 2020-12-12 11:30

    An interesting library that handles this (and avoids a lot of other boilerplate) is attrs. Your example, for instance, could be reduced to this (assume the class is called MyClass):

    import attr
    
    @attr.s
    class MyClass:
        x = attr.ib()
        y = attr.ib()
        z = attr.ib()
    

    You don't even need an __init__ method anymore, unless it does other stuff as well. Here's a nice introduction by Glyph Lefkowitz.

    0 讨论(0)
  • 2020-12-12 11:32

    Python 3.7 onwards

    In Python 3.7, you may (ab)use the dataclass decorator, available from the dataclasses module. From the documentation:

    This module provides a decorator and functions for automatically adding generated special methods such as __init__() and __repr__() to user-defined classes. It was originally described in PEP 557.

    The member variables to use in these generated methods are defined using PEP 526 type annotations. For example this code:

    @dataclass
    class InventoryItem:
        '''Class for keeping track of an item in inventory.'''
        name: str
        unit_price: float
        quantity_on_hand: int = 0
    
        def total_cost(self) -> float:
            return self.unit_price * self.quantity_on_hand
    

    Will add, among other things, a __init__() that looks like:

    def __init__(self, name: str, unit_price: float, quantity_on_hand: int=0):
          self.name = name
          self.unit_price = unit_price
          self.quantity_on_hand = quantity_on_hand
    

    Note that this method is automatically added to the class: it is not directly specified in the InventoryItem definition shown above.

    If your class is large and complex, it may be inappropriate to use a dataclass. I'm writing this on the day of release of Python 3.7.0, so usage patterns are not yet well established.

    0 讨论(0)
  • 2020-12-12 11:33

    It's a natural way to do things in Python. Don't try to invent something more clever, it will lead to overly clever code that no one on your team will understand. If you want to be a team player and then keep writing it this way.

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