python 如何将__init__参数自动分配给属性?

eulz3vhy  于 12个月前  发布在  Python
关注(0)|答案(6)|浏览(79)

有没有一种方法可以自动绑定到__init__方法的参数?
例如,假设我们有:

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

是否可以创建一个lazy_init装饰器,允许以这种方式来实现?

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

是否存在类似的技术?还是有一个很好的理由不去尝试?

rqqzpn5f

rqqzpn5f1#

供将来遇到此问题的所有其他人参考(Python 3.7或更高版本):有一个dataclass装饰器可以做到这一点。

示例

from dataclasses import dataclass

@dataclass
class Person:
    name
    age
    address

# other methods...
aoyhnmkz

aoyhnmkz2#

这绝对是可能的,这里有一个有点天真的实现:

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

一旦你开始有一些除了属性之外的Map到属性的东西,你就会遇到麻烦。您至少需要某种方式来指定哪些参数要初始化为属性…到时候只会变得更麻烦

vql8enpb

vql8enpb3#

你可以像这样使用Python 3.7的数据类:

from dataclasses import dataclass

@dataclass
class MyClass:
    var_1: str
    var_2: float

或者你可以像这样使用fastcore库中的store_attr()方法:

from fastcore.utils import store_attr

class MyClass:
    def __init__(self, var_1, var_2):
        store_attr()

这两种方法都产生等价的init方法:

def __init__(self, var_1, var_2):
    self.var_1 = var_1
    self.var_2 = var_2
2nc8po8w

2nc8po8w5#

下面是最简单的形式:

def lazy_init(*param_names):
  def ret(old_init):
    def __init__(self, *args, **kwargs):
      if len(args) > len(param_names):
        raise TypeError("Too many arguments")
      for k in kwargs:
        if k not in param_names:
          raise TypeError("Arg %r unexpected" % k)
      for par, arg in zip(param_names, args):
        setattr(self, par, arg)
      for par, arg in kwargs.items():
        setattr(self, par, arg)
      old_init(*args, **kwargs)
    return __init__
    #
  return ret

class Q(object):
  @lazy_init("a", "b")
  def __init__(self, *args, **kwargs):
    print "Original init"

>>> q = Q(1, 2)
Original init
>>> q.a, q.b
(1, 2)

考虑制作一个类装饰器来覆盖__str__

aor9mmx1

aor9mmx16#

initify.py由我创建:)它做的正是你所期望的。

class Animal:
   @init_args(exclude=["name"])
   def __init__(self, name):
       pass

或使用默认值:

class Animal:
   @init_args
   def __init__(self, name="Default name"):
       pass

甚至排除继承:

class Dog(Animal):
   @init_args(exclude=["name"])
   def __init__(self, species):
       pass

https://github.com/prankymat/initify.py

相关问题