Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python decorator for automatic binding __init__ arguments

Is there a way to automatically bind to self (some of) the arguments of the __init__ method?

I mean something like:

class Person:
    @lazy_init
    def __init__(self, name, age, address):
        ...

... instead of:

class Person:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address
        ...

I wonder if there is something similar that people already use in such cases. Or there is any reason I'm not supposed to do it this way in the first place?

like image 425
André Lima Avatar asked Feb 19 '11 01:02

André Lima


People also ask

Can Python decorators take arguments?

The decorator arguments are accessible to the inner decorator through a closure, exactly like how the wrapped() inner function can access f . And since closures extend to all the levels of inner functions, arg is also accessible from within wrapped() if necessary.

How do you pass a value dynamically in Python?

Dynamic Function Arguments We simply can make solve_for() accept *args and **kwargs then pass that to func() . Of course, you will need to handle the arguments in the function that will be called.

Can decorators be chained?

Chaining decorators means applying more than one decorator inside a function. Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together. It is also known as nested decorators in Python.


2 Answers

It's definitely possible, here's a somewhat naive implementation:

from functools import wraps

def lazy_init(init):
    import inspect
    arg_names = inspect.getargspec(init)[0]

    @wraps(init)
    def new_init(self, *args):
        for name, value in zip(arg_names[1:], args):
            setattr(self, name, value)
        init(self, *args)

    return new_init

class Person:
    @lazy_init
    def __init__(self, name, age):
        pass

p = Person("Derp", 13)
print p.name, p.age

Once you start having something besides attributes that map to properties, you're going to run into trouble though. You'll need at least some way to specify which args to initialize as properties... at which point it'll just become more hassle than it's worth.

like image 141
Matti Virkkunen Avatar answered Oct 19 '22 09:10

Matti Virkkunen


For future reference for all others who encounter this question in the future (Python 3.7 or above): There is the dataclass decorator that does exactly this.

Example

from dataclasses import dataclass

@dataclass
class Person:
    name
    age
    address

# other methods...
like image 22
Adrian Avatar answered Oct 19 '22 09:10

Adrian