Is there any cool way to express `if x is None: x = self.x` in python class?

179 views Asked by At

I'm just studying python OOP, and truly confused when to use self and not.

especially when I want to make a method that defaultly get object instance input and also wanna make it work as a normal method that can get the input of custom parameters, I get somewhat bothersome to type if x is None: x = self.x for all the parameters of the method.

Example is as follows.

from dataclasses import dataclass

@dataclass
class PlusCalculator():
    x: int = 1
    y: int = 2
    result: int = None
    
    def plus(self, x=None, y=None):
        if x is None: x = self.x    ## the bothersome part
        if y is None: y = self.y
        
        result = x + y
        self.result = result        ## also confusing...is it good way?
        
        return result
    
pc = PlusCalculator()
print(pc.plus())
print(pc.plus(4,5))

Is there any good way to use instance variables as default value of function parameter??

2

There are 2 answers

2
Ian Price On BEST ANSWER

A conditional expression is readable, fast, and intuitive-

x = self.x if x is None else x

Re:

Is there any good way to use instance variables as default value of function parameter??

Regarding the setting of self.result- This should be avoided unless you need to access it as an instance variable later. As such you can simplify this to:

@dataclass
class PlusCalculator():
    ...

    def plus(self, x=None, y=None):
        x = self.x if x is None else x
        y = self.y if y is None else y
        return x + y
0
blhsing On

One cleaner approach would be to create a method decorator such that when the wrapped method is called, arguments with a default value would be applied with values of attributes of the self object under the same names as parameters declared with a default value of an object meant as a default value to be replaced. I recommend that this object be a new object (named DEFAULT in the following example) dedicated for this purpose instead of None so that None can actually be used as an intended argument value for the decorated method:

from inspect import signature

def default_self(method):
    def wrapper(self, *args, **kwargs):
        bound = sig.bind(self, *args, **kwargs)
        bound.apply_defaults()
        for name in names:
            if bound.arguments[name] is DEFAULT:
                bound.arguments[name] = getattr(self, name)
        return method(*bound.args, **bound.kwargs)

    sig = signature(method)
    names = [name for name, param in sig.parameters.items() if param.default is DEFAULT]
    return wrapper

DEFAULT = object()

so that:

@dataclass
class PlusCalculator:
    x: int = 1
    y: int = 2
    result: int = None

    @default_self
    def plus(self, x=DEFAULT, y=DEFAULT):
        self.result = x + y # an intermediate variable is unnecessary
        return self.result

pc = PlusCalculator()
print(pc.plus())
print(pc.plus(4, 5))
print(pc.plus(y=3))
print(pc.plus(x=2, y=3))

outputs:

3
9
4
5

Demo: https://ideone.com/joPRKD