如何使用实例的属性作为方法的默认参数?

发布于 2024-12-15 21:19:01 字数 494 浏览 1 评论 0原文

我想使用实例的属性值将默认参数传递给实例方法:

class C:
    def __init__(self, format):
        self.format = format

    def process(self, formatting=self.format):
        print(formatting)

尝试这样做时,我收到以下错误消息:

NameError: name 'self' is not defined

我希望该方法的行为如下:

C("abc").process()       # prints "abc"
C("abc").process("xyz")  # prints "xyz"

这里有什么问题,为什么会这样不工作?我怎样才能做到这一点?

I want to pass a default argument to an instance method using the value of an attribute of the instance:

class C:
    def __init__(self, format):
        self.format = format

    def process(self, formatting=self.format):
        print(formatting)

When trying that, I get the following error message:

NameError: name 'self' is not defined

I want the method to behave like this:

C("abc").process()       # prints "abc"
C("abc").process("xyz")  # prints "xyz"

What is the problem here, why does this not work? And how could I make this work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

韵柒 2024-12-22 21:19:01

您不能真正将其定义为默认值,因为在定义方法时(在任何实例存在之前)都会评估默认值。通常的模式是执行类似以下操作:

class C:
    def __init__(self, format):
        self.format = format

    def process(self, formatting=None):
        if formatting is None:
            formatting = self.format
        print(formatting)

仅当 formattingNone 时才会使用 self.format


要演示默认值如何工作的要点,请参见以下示例:

def mk_default():
    print("mk_default has been called!")

def myfun(foo=mk_default()):
    print("myfun has been called.")

print("about to test functions")
myfun("testing")
myfun("testing again")

以及此处的输出:

mk_default has been called!
about to test functions
myfun has been called.
myfun has been called.

请注意 mk_default 是如何仅被调用一次的,而且是在调用该函数之前发生的!

You can't really define this as the default value, since the default value is evaluated when the method is defined which is before any instances exist. The usual pattern is to do something like this instead:

class C:
    def __init__(self, format):
        self.format = format

    def process(self, formatting=None):
        if formatting is None:
            formatting = self.format
        print(formatting)

self.format will only be used if formatting is None.


To demonstrate the point of how default values work, see this example:

def mk_default():
    print("mk_default has been called!")

def myfun(foo=mk_default()):
    print("myfun has been called.")

print("about to test functions")
myfun("testing")
myfun("testing again")

And the output here:

mk_default has been called!
about to test functions
myfun has been called.
myfun has been called.

Notice how mk_default was called only once, and that happened before the function was ever called!

绻影浮沉 2024-12-22 21:19:01

在 Python 中,名称 self 并不特殊。这只是参数名称的约定,这就是为什么__init__中有一个self参数。 (实际上,__init__也不是很特别,特别是它没有实际创建对象......这是一个更长的故事)

C("abc" ).process() 创建一个 C 实例,在 C 类中查找 process 方法,并使用以下命令调用该方法C 实例作为第一个参数。因此,如果您提供了它,它最终会出现在 self 参数中。

即使您有该参数,您也不会被允许编写类似 def process(self,formatting = self.formatting) 的内容,因为 self 不在范围内但在您设置默认值的地方。在Python中,参数的默认值是在编译函数时计算的,并“粘在”函数中。 (这就是为什么如果您使用像 [] 这样的默认值,该列表将记住函数调用之间的更改的原因。)

我怎样才能做到这一点?

传统方法是使用 None 作为默认值,检查该值并在函数内替换它。您可能会发现为此目的创建一个特殊值会更安全一些(您只需要一个 object 实例,只要隐藏它以便调用代码不使用相同的实例)而不是。无论哪种方式,您都应该使用 is 检查该值,而不是 ==

In Python, the name self is not special. It's just a convention for the parameter name, which is why there is a self parameter in __init__. (Actually, __init__ is not very special either, and in particular it does not actually create the object... that's a longer story)

C("abc").process() creates a C instance, looks up the process method in the C class, and calls that method with the C instance as the first parameter. So it will end up in the self parameter if you provided it.

Even if you had that parameter, though, you would not be allowed to write something like def process(self, formatting = self.formatting), because self is not in scope yet at the point where you set the default value. In Python, the default value for a parameter is calculated when the function is compiled, and "stuck" to the function. (This is the same reason why, if you use a default like [], that list will remember changes between calls to the function.)

How could I make this work?

The traditional way is to use None as a default, and check for that value and replace it inside the function. You may find it is a little safer to make a special value for the purpose (an object instance is all you need, as long as you hide it so that the calling code does not use the same instance) instead of None. Either way, you should check for this value with is, not ==.

掩于岁月 2024-12-22 21:19:01

由于您想使用 self.format 作为默认参数,这意味着该方法需要是特定于实例的(即无法在类级别定义它)。相反,您可以在类的 __init__ 期间定义特定方法。您可以在此处访问实例特定属性。

一种方法是使用 functools.partial 以获得方法的更新(特定)版本:

from functools import partial


class C:
    def __init__(self, format):
        self.format = format
        self.process = partial(self.process, formatting=self.format)

    def process(self, formatting):
        print(formatting)


c = C('default')
c.process()
# c.process('custom')  # Doesn't work!
c.process(formatting='custom')

请注意,使用这种方法,您只能通过关键字传递相应的参数,因为如果您按位置提供它,这会在 partial< 中产生冲突/代码>。

另一种方法是在 __init__ 中定义和设置方法:

from types import MethodType


class C:
    def __init__(self, format):
        self.format = format

        def process(self, formatting=self.format):
            print(formatting)

        self.process = MethodType(process, self)


c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')

这也允许按位置传递参数,但是方法解析顺序变得不太明显(例如,这可能会影响 IDE 检查,但我认为有是 IDE 特定的解决方法)。

另一种方法是为这些“实例属性默认值”创建一个自定义类型,并使用一个特殊的装饰器来执行相应的 getattr 参数填充:

import inspect


class Attribute:
    def __init__(self, name):
        self.name = name


def decorator(method):
    signature = inspect.signature(method)

    def wrapper(self, *args, **kwargs):
        bound = signature.bind(*((self,) + args), **kwargs)
        bound.apply_defaults()
        bound.arguments.update({k: getattr(self, v.name) for k, v in bound.arguments.items()
                                if isinstance(v, Attribute)})
        return method(*bound.args, **bound.kwargs)

    return wrapper


class C:
    def __init__(self, format):
        self.format = format

    @decorator
    def process(self, formatting=Attribute('format')):
        print(formatting)


c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')

Since you want to use self.format as a default argument this implies that the method needs to be instance specific (i.e. there is no way to define this at class level). Instead you can define the specific method during the class' __init__ for example. This is where you have access to instance specific attributes.

One approach is to use functools.partial in order to obtain an updated (specific) version of the method:

from functools import partial


class C:
    def __init__(self, format):
        self.format = format
        self.process = partial(self.process, formatting=self.format)

    def process(self, formatting):
        print(formatting)


c = C('default')
c.process()
# c.process('custom')  # Doesn't work!
c.process(formatting='custom')

Note that with this approach you can only pass the corresponding argument by keyword, since if you provided it by position, this would create a conflict in partial.

Another approach is to define and set the method in __init__:

from types import MethodType


class C:
    def __init__(self, format):
        self.format = format

        def process(self, formatting=self.format):
            print(formatting)

        self.process = MethodType(process, self)


c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')

This allows also passing the argument by position, however the method resolution order becomes less apparent (which can affect the IDE inspection for example, but I suppose there are IDE specific workarounds for that).

Another approach would be to create a custom type for these kind of "instance attribute defaults" together with a special decorator that performs the corresponding getattr argument filling:

import inspect


class Attribute:
    def __init__(self, name):
        self.name = name


def decorator(method):
    signature = inspect.signature(method)

    def wrapper(self, *args, **kwargs):
        bound = signature.bind(*((self,) + args), **kwargs)
        bound.apply_defaults()
        bound.arguments.update({k: getattr(self, v.name) for k, v in bound.arguments.items()
                                if isinstance(v, Attribute)})
        return method(*bound.args, **bound.kwargs)

    return wrapper


class C:
    def __init__(self, format):
        self.format = format

    @decorator
    def process(self, formatting=Attribute('format')):
        print(formatting)


c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')
瞳孔里扚悲伤 2024-12-22 21:19:01

您无法在方法定义中访问 self 。我的解决方法是这样的 -

class Test:
  def __init__(self):
    self.default_v = 20

  def test(self, v=None):
    v = v or self.default_v
    print(v)

Test().test()
> 20

Test().test(10)
> 10

You can't access self in the method definition. My workaround is this -

class Test:
  def __init__(self):
    self.default_v = 20

  def test(self, v=None):
    v = v or self.default_v
    print(v)

Test().test()
> 20

Test().test(10)
> 10
情绪 2024-12-22 21:19:01

如果您希望它们充当非静态方法,则需要将“self”作为第一个参数传递给任何类函数。

它指的是对象本身。您无法将“self”作为默认参数传递,因为它的位置已固定为第一个参数。

在您的情况下,不要使用“formatting=self.format”,而是使用“formatting=None”,然后从代码中分配值,如下所示:

[编辑]

class c:
        def __init__(self, cformat):
            self.cformat = cformat

        def process(self, formatting=None):
            print "Formating---",formatting
            if formatting == None:
                formatting = self.cformat
                print formatting
                return formatting
            else:
                print formatting
                return formatting

c("abc").process()          # prints "abc"
c("abc").process("xyz")     # prints "xyz"

注意:不要使用“format”作为变量名,因为它是内置函数在Python中

"self" need to be pass as the first argument to any class functions if you want them to behave as non-static methods.

it refers to the object itself. You could not pass "self" as default argument as it's position is fix as first argument.

In your case instead of "formatting=self.format" use "formatting=None" and then assign value from code as below:

[EDIT]

class c:
        def __init__(self, cformat):
            self.cformat = cformat

        def process(self, formatting=None):
            print "Formating---",formatting
            if formatting == None:
                formatting = self.cformat
                print formatting
                return formatting
            else:
                print formatting
                return formatting

c("abc").process()          # prints "abc"
c("abc").process("xyz")     # prints "xyz"

Note : do not use "format" as variable name, 'cause it is built-in function in python

菩提树下叶撕阳。 2024-12-22 21:19:01

您可以使用“默认”字典并使用 eval() 创建类的新实例,而不是创建涵盖默认争论的 if-then 列表:

class foo():
    def __init__(self,arg):
        self.arg = arg

class bar():
    def __init__(self,*args,**kwargs):
        #default values are given in a dictionary
        defaults = {'foo1':'foo()','foo2':'foo()'}
        for key in defaults.keys():

            #if key is passed through kwargs, use that value of that key
            if key in kwargs: setattr(self,key,kwargs[key]) 

            #if no key is not passed through kwargs
            #create a new instance of the default value
            else: setattr(self,key, eval(defaults[key]))

我将其放在实例化另一个类的每个类的开头类作为默认参数。它避免了 python 在编译时评估默认值...我想要一种更干净的 pythonic 方法,但是 lo'。

Instead of creating a list of if-thens that span your default arguements, one can make use of a 'defaults' dictionary and create new instances of a class by using eval():

class foo():
    def __init__(self,arg):
        self.arg = arg

class bar():
    def __init__(self,*args,**kwargs):
        #default values are given in a dictionary
        defaults = {'foo1':'foo()','foo2':'foo()'}
        for key in defaults.keys():

            #if key is passed through kwargs, use that value of that key
            if key in kwargs: setattr(self,key,kwargs[key]) 

            #if no key is not passed through kwargs
            #create a new instance of the default value
            else: setattr(self,key, eval(defaults[key]))

I throw this at the beginning of every class that instantiates another class as a default argument. It avoids python evaluating the default at compile... I would love a cleaner pythonic approach, but lo'.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文