Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a shorthand initializer in Python?

Tags:

python

I have found no reference for a short constructor call that would initialize variables of the caller's choice. I am looking for

class AClass:
    def __init__(self):
        pass

instance = AClass(var1=3, var2=5)

instead of writing the heavier

class AClass:
    def __init__(self, var1, var2):
        self.var1 = var1
        self.var2 = var2

or the much heavier

instance = AClass()
instance.var1 = 3
instance.var2 = 5

Am I missing something?

like image 530
PPC Avatar asked Jan 16 '18 15:01

PPC


People also ask

What is the __ init __ in Python?

The __init__ method is the Python equivalent of the C++ constructor in an object-oriented approach. The __init__ function is called every time an object is created from a class. The __init__ method lets the class initialize the object's attributes and serves no other purpose. It is only used within classes.

What is __ init __( self in Python?

The self in keyword in Python is used to all the instances in a class. By using the self keyword, one can easily access all the instances defined within a class, including its methods and attributes. init. __init__ is one of the reserved methods in Python. In object oriented programming, it is known as a constructor.

Does Python automatically initialize variables?

If a class defines an __init__() method, when you create a class instance (an object), Python automatically calls __init__ function only once to initializing an object's variables.

Does Python need initialization?

As soon as we set a variable equal to a value, we initialize or create that variable. Once we have done that, we are set to use the variable instead of the value. In Python, variables do not need explicit declaration prior to use like some programming languages; you can start using the variable right away.


2 Answers

This is an excellent question and has been a puzzle also for me.

In the modern Python world, there are three (excellent) shorthand initializers (this term is clever, I am adopting it), depending on your needs. None requires any footwork with __init__ methods (which is what you wanted to avoid in the first place).

Namespace object

If you wish to assign arbitrary values to an instance (i.e. not enforced by the class), you should use a particular data structure called namespace. A namespace object is an object accessible with the dot notation, to which you can assign basically what you want.

You can import the Namespace class from argparse (it is covered here: How do I create a Python namespace (argparse.parse_args value)?). Since Python 3.3. a SimpleNamespace class is available from the standard types package.

from types import SimpleNamespace
instance = SimpleNamespace(var1=var1, var2=var2)

You can also write:

instance = SimpleNamespace()
instance.var1 = var1
instance.var2 = var2

Let's say its the "quick and dirty way", which would work in a number of cases. In general there is not even the need to declare your class.

If you want your instances to still have a few methods and properties you could still do:

class AClass(Namespace):
    def mymethod(self, ...):
        pass

And then:

instance = AClass(var1=var1, var2=var2, etc.)

That gives you maximum flexibility.

Named tuple

On the other hand, if you want the class to enforce those attributes, then you have another, more solid option.

A named tuple produces immutable instances, which are initialized once and for all. Think of them as ordinary tuples, but with each item also accessible with the dot notation. This class namedtuple is part of the standard distribution of Python. This how you generate your class:

from collections import namedtuple
AClass = namedtuple("AClass", "var1 var2")

Note how cool and short the definition is and not __init__ method required. You can actually complete your class after that.

And to create an object:

instance = AClass(var1, var2)

or

instance = AClass(var1=var1, var2=var2)

Named list

But what if you want that instance to be mutable, i.e. to allow you update the properties of the instance? The answer is the named list (also known as RecordClass). Conceptually it is like a normal list, where the items are also accessible with the dot notation.

There are various implementations. I personally use the aptly named namedlist.

The syntax is identical:

from namedlist import namedlist
AClass = namedlist("AClass", "var1 var2")

And to create an object:

instance = AClass(var1, var2)

or:

instance = AClass(var1=var1, var2=var2)

And you can then modify them:

instance.var1 = var3

But you can't add an attribute that is not defined.

>>> instance.var4 = var4
  File "<stdin>", line 1, in <module>
AttributeError: 'instance' object has no attribute 'var4'

Usage

Here is my two-bit:

  1. Namespace object is for maximum flexibility and there is not even the need to declare a class; with the risk of having instances that don't behave properly (but Python is a language for consenting adults). If you have only one instance and/or you know what you're doing, that would be the way to go.

  2. namedtuple class generator is perfect to generate objects for returns from functions (see this brief explanation in a lecture from Raymond Hettinger). Rather than returning bland tuples that the user needs to look up in the documentation, the tuple returned is self-explanatory (a dir or help will do it). And it it's compatible with tuple usage anyway (e.g. k,v, z = my_func()). Plus it's immutable, which has its own advantages.

  3. namedlist class generator is useful in a wide range of cases, including when you need to return multiple values from a function, which then need to be amended at a later stage (and you can still unpack them: k, v, z = instance). If you need a mutable object from a proper class with enforced attributes, that might be the go-to solution.

If you use them well, this might significantly cut down time spent on writing classes and handling instances!


Update (September 2020)

@PPC: your dream has come true.

Since Python 3.7, a new tool is available as a standard: dataclasses (unsurprisingly, the designer of the named list package, Eric V. Smith, is also behind it).

In essence, it provides an automatic initialization of class variables.

from dataclasses import dataclass

@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

(from the official doc)

What the @dataclass decorator will do, will be to automatically add the __init__() method:

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

IMHO, it's a pretty, eminently pythonic solution.

Eric also maintains a backport of dataclasses on github, for Python 3.6.

like image 64
fralau Avatar answered Sep 27 '22 16:09

fralau


You can update the __dict__ attribute of your object directly, which is where the attributes are stored

class AClass:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

c = AClass(var1=1, var2='a')
like image 45
Patrick Haugh Avatar answered Sep 27 '22 16:09

Patrick Haugh