14

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?

2

4 Answers 4

21

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.

1
  • 1
    Thanks for the update (yours to stackoverflow and that of EV Smith for the code) :)
    – PPC
    Commented Apr 26, 2021 at 10:57
11

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')
3
  • 3
    And the price you pay is that you don't get error checking for constructor arguments any more. Commented Jan 16, 2018 at 15:53
  • @DietrichEpp You could always insert some validation step where you do stuff to clean up/check kwargs before the update. Commented Jan 16, 2018 at 15:55
  • This does what OP is asking for, but would likely be considered poor style in Python. One of the "Zen of Python" python.org/dev/peps/pep-0020 principles is "explicit is better than implicit" and automatically assigning nameless attributes is not explicit.
    – bjmc
    Commented Jan 16, 2018 at 15:55
1

You can use the dictionary representation of the object's attributes, and update its elements with the keyword arguments given to the constructor:

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

instance = AClass(var1=3, var2=5)
print(instance.var1, instance.var2) # prints 3 5

However, consider this question and its answers considering the style of this. Unless you know what you are doing, better explicitly set the arguments one by one. It will be better understandable for you and other people later - explicit is better than implicit. If you do it the __dict__.update way, document it properly.

-3

Try

class AClass:
    def __init__(self, **vars):
        self.var1 = vars.get('var1')
1
  • This only saves writing the attribute names in the method definition. You're still stuck writing a bunch of assignments self.var1=, self.var2=...
    – bjmc
    Commented Jan 16, 2018 at 15:57

Not the answer you're looking for? Browse other questions tagged or ask your own question.