【问题标题】:Access to __init__ arguments访问 __init__ 参数
【发布时间】:2012-07-18 18:18:15
【问题描述】:

是否可以访问传递给__init__ 的参数,而无需显式存储它们?

例如

class thing(object):
  def __init__(self, name, data):
    pass # do something useful here

t = thing('test', [1,2,3,])
print t.__args__ # doesn't exist

>> ('test', [1,2,3])

这个用例是创建一个超类,它可以自动存储用于创建从它派生的类的实例的参数,不必将所有参数显式传递给超级的__init__。也许有更简单的方法!

【问题讨论】:

  • 我在这里的回答中解释了初始化对象的各种模式:stackoverflow.com/a/2353165/92493
  • 谢谢 Otto,但我认为这些模式中的任何一个都与我的问题无关。
  • 我假设您的意思是 class thing 而不是 def thing
  • 抱歉,是的,将其更改为更简单的示例并在此过程中输入错误。现已修复。

标签: python


【解决方案1】:

不,您必须存储它们。否则,它们在__init__() 返回后消失,作为所有局部变量。

如果您不想显式传递所有参数,可以使用**kwargs

class Base(object):
    def __init__(self, name, data):
        # store name and data

class Derived(Base):
    def __init__(self, **kwargs):
        Base.__init__(self, **kwargs)

Derived(name="Peter", data=42)

【讨论】:

  • 这正是我目前正在做的事情,但我想知道是否有一种方法不依赖派生类的作者正确传递所有 *args 和 **kwargs。由于 Derived.__init__ 没有在 Base.__init__ 的作用域内返回,有没有办法检索那些本地人?
  • @lost:在支持堆栈帧的 Python 实现中(CPython 支持),您可以检查调用帧并提取它们的局部变量,但您绝对不想走那条路。我还是不明白你在解决什么问题。当然,派生类必须正确调用基类的方法。所有人都必须这样做。
  • 当然,但是如果你想提供一个“神奇”的基类,以便派生类“正常工作”,你不宁愿派生类甚至不必调用方法吗?但是,您的回答让我明白了为什么像 __args__ 这样的东西不存在,所以谢谢!
【解决方案2】:

这并不完全推荐,但这里有一个自动存储参数变量的包装器:

from functools import wraps
def init_wrapper(f):
    @wraps(f)
    def wrapper(self, *args, **kwargs):
        func_parameters = f.func_code.co_varnames[1:f.func_code.co_argcount]

        #deal with default args
        diff = len(func_parameters) - len(args)
        if diff > 0:
            args += f.func_defaults[-diff:]

        #set instance variables
        for pos, arg in enumerate(func_parameters):
            print pos, arg
            setattr(self, arg, args[pos])

        f(self, *args, **kwargs) #not necessary to use return on __init__()
    return wrapper

用法:

class A(object):
    @init_wrapper
    def __init__(self, a, b, c):
        print a + b + c

例子:

>>> a = A(1, 2, 3)
6
>>> a.a
1
>>> a.b
2
>>> a.c
3

【讨论】:

  • 啊,我想知道有什么方法可以使用代码对象。感谢演示 - 并同意“不完全推荐”的警告!
【解决方案3】:

一句话:没有。

你可以做的是:

def __init__(self, *args, **kwargs):
    self.args = args
    self.kwargs = kwargs

如果您发现自己经常需要这样做,您还可以使用装饰器来抽象任务。

【讨论】:

    【解决方案4】:

    我认为您正在寻找 arbitrary argument listskeyword arguments 以及 super.__init__

    不过,在开始这条路之前,请先阅读 "Python's Super is nifty, but you can't use it"

    【讨论】:

    • 不反对,但它有什么帮助并不明显。
    • 文章不仅不相关,而且包含一些相当糟糕的建议(例如始终将**kwargs传递给super__init__
    • 同意这似乎没有帮助。我绝对可以将参数传递给 super 的__init__,但我宁愿避免它(尽管公平地说,这可能从原始问题中不清楚)。但是,您确实链接到 Python 的函数调用语法,我一直想知道它在哪里被正确记录:-(
    猜你喜欢
    • 2021-11-25
    • 2011-01-11
    • 1970-01-01
    • 2020-08-16
    • 2010-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多