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?
In Python, the name
self
is not special. It's just a convention for the parameter name, which is why there is aself
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 aC
instance, looks up theprocess
method in theC
class, and calls that method with theC
instance as the first parameter. So it will end up in theself
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)
, becauseself
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.)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 (anobject
instance is all you need, as long as you hide it so that the calling code does not use the same instance) instead ofNone
. Either way, you should check for this value withis
, not==
.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. An easy work-around is to do something like this:
self.format
will only be used ifformatting
isNone
.To demonstrate the point of how default values work, see this example:
And the output here:
Notice how
mk_default
was called only once, and that happened before the function was ever called!"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]
Note : do not use "format" as variable name, 'cause it is built-in function in python
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: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__
: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: