Calling Python constructor with default arguments - python

Suppose you have a Python class whose constructor looks something like this:
def __init__(self,fname=None,data=[],imobj=None,height=0,width=0):
and you want to create an instance of it but only provide the fname and imobj inputs. Would the correct way to do this be
thing = Thing(f_name, None, im_obj, None, None)
or is there a preferred way of making this call?

You can just do:
thing = Thing(f_name=value1, im_obj=value2)
Note that you do not actually need the f_name= in this case since fname is the first parameter (besides self, which is passed implicitly). You could just do:
thing = Thing(value1, im_obj=value2)
But I personally think that the first solution is more readable. It makes it clear that we are only changing the values of f_name and im_obj while leaving every other parameter to its default value. In addition, it keeps people from wondering what parameter value1 will be assigned to.
Also, you almost never want to have a mutable object such as a list be a default argument. It will be shared across all calls of the function. For more information, see "Least Astonishment" and the Mutable Default Argument

You can instanciate with:
thing = Thing(f_name, imobj=im_obj)
Other named arguments will be set to default.
You can also pass a dict to the constructor:
>>> argDict={"fname": f_name, "imobj": im_obj}
>>> thing = Thing(**argDict)
This will unpack the dict values. See keyword arguments.

Related

Why should I set a function list argument as empty or none instead of using a normal variable

I've been looking up the information about why people should not use empty list as a function argument and pass none type as an argument and came up with a few questions.
Here is my code:
def add_employee(emp, emp_list=None):
if emp_list is None:
emp_list = []
emp_list.append(emp)
print(emp_list)
And here is code without second argument's type specified:
def add_employee(emp, emp_list):
emp_list.append(emp)
return emp_list
When I do not define emp_list as an empty list or none I can not utilize function's deafualt argument behavior: I can't call it like add_employee('Mark'), I had to add second variable to pass. Why is it good to have that backup default behaviour? Why couldn't I just leave it as emp_list.
Here is a great explanation of why you should avoid using mutable arguments in your defaults. Or at least use them sparingly: link
The general gist of it that the list will be created for the first time (in a location in memory) when you define the function. So as python reads the code and interprets it, you will end up creating that list once on the first 'read' of the function.
What this means is that you are not creating a fresh list each time you call that function, only when you define it.
To illustrate I will use the example from the link I shared above:
def some_func(default_arg=[]):
default_arg.append("some_string")
return default_arg
>>> some_func()
['some_string']
>>> some_func()
['some_string', 'some_string']
>>> some_func([])
['some_string']
>>> some_func()
['some_string', 'some_string', 'some_string']
If I understood your question correctly, you are asking why you're better off defining the emp_list explicitly rather than having it outside the function. In my mind it boils down to encapsulation. You're essentially trying to make sure that your function doesn't change the behavior of anything outside of its scope so you're forced to pass it things directly and be explicit. In practice if you have a variable outside of the scope named emp_list, it is absolutely fine to just append to it as long as you understand what the expected behavior is.
If you pass a list in the first bit of code as the emp_list, then the variable emp_list will contain your list a. The if statement will check if list a is None and since that check fails, it will skip the next line of assigning it a fresh empty list.

Python: explicitly use default arguments

Under normal circumstances one calls a function with its default arguments by omitting those arguments. However if I'm generating arguments on the fly, omitting one isn't always easy or elegant. Is there a way to use a function's default argument explicitly? That is, to pass an argument which points back to the default argument.
So something like this except with ~use default~ replaced with something intelligent.
def function(arg='default'):
print(arg)
arg_list= ['not_default', ~use default~ ]
for arg in arg_list:
function(arg=arg)
# output:
# not_default
# default
I don't know if it's even possible and given the term "default argument" all my searches come up with is coders first tutorial. If this functionality is not supported that's ok too, I'd just like to know.
Unfortunately there is no such feature in Python. There are hackarounds, but they're not very likable.
The simple and popular pattern is to move the default into the function body:
def function(arg=None):
if arg is None:
arg = 'default'
...
Now you can either omit the argument or pass arg=None explicitly to take on the default value.
There is no general purpose way to omit an argument; you can specialize to particular functions by explicitly passing the appropriate default value, but otherwise, your only option is to fail to pass the argument.
The closest you could come is to replace your individual values with tuples or dicts that omit the relevant argument, then unpack them at call time. So for your example, you'd change arglist's definition to:
arg_list = [{'arg': 'not_default'}, {}]
then use it like so:
for arg in arg_list:
function(**arg)
A slightly uglier approach is to use a sentinel when you don't want to pass the argument, use that in your arg_list, and test for it, e.g.:
USEDEFAULT = object()
arg_list = ['not_default', USEDEFAULT]
for arg in arg_list:
if arg is USEDEFAULT:
function()
else:
function(arg=arg)
Obviously a bit less clean, but possibly more appropriate for specific use cases.

Python: call function with default arguments that come before positional arguments

For example, I'd like to do something like: greet(,'hola'), where greet is:
def greet(person='stranger', greeting='hello')
This would help greatly for testing while writing code
Upon calling a function you can use the variable names to make it even more clear what variable will assume which value. At the same time, if defaults are provided in the function definition, skipping variables when calling the function does not raise any errors. So, in short you can just do this:
def greet(person='stranger', greeting='hello')
print('{} {}'.format(greeting, person))
return
greet(greeting='hola') # same as greet(person='stranger', greeting='hola')
# returns 'hola stranger'
Note that, as I said above this would not work if for example your function definition was like this:
def greet(person, greeting)
print('{} {}'.format(greeting, person))
return
Since in this case, Python would complain saying that it does not know what to do with person; no default is supplied..
And by the way, the problem you are describing is most likely the very reason defaults are used in the first place
Without knowing the other parameters, and only knowing that the parameter you want to change is in second position you could use the inspect module to get function signature & associated default values.
Then make a copy of the default values list and change the one at the index you want:
import inspect
def greet(person='stranger', greeting='hello'):
print(person,greeting)
argspec = inspect.getargspec(greet)
defaults = list(argspec.defaults)
defaults[1] = "hola" # change second default parameter
greet(**dict(zip(argspec.args,defaults)))
Assuming that all parameters have default values (else it shifts the lists an that fails) that prints:
stranger hola

How do you get a method whose code instantiates another object to initialize a different object each time its called? [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
Sorry if the question isn't that clear in the title but I had a limited amount of space,
To clarify...
In my code I have a class called empDB that creates an empty list for storing objects.
Too add new objects to the list I created a method in empDB called appendEmp. appendEmp takes one parameter that specifies the class which it will instantiate and then add it to the list. If no parameter is provided it defaults to the class Employee().
here is my problem
when I call the method appendEmp it creates new objects in the list, however when I try to instantiate an object thats already in the list it will only create an alias of it. I need it to create a new one each time so that i can edit the attributes of each object in the list individually.
here is a snippet of my code:
class empDB:
def __init__(self, lst= []): #creates empty list
self.lst = lst
def appendEmp(self, emp=Employee()):
self.lst=self.lst+[emp]
basicly this is what happens now
>>> db=empDB()
>>> db.appendEmp()
>>> db.appendEmp()
>>> db[0] is db[1]
True
I need it to be false
You can solve your problem like that:
class empDB:
def __init__(self, lst= []): #creates empty list
self.lst = lst
def appendEmp(self, emp=None):
if emp is None:
empt = Employee()
self.lst=self.lst+[emp]
The issue was caused by mutable value being assigned as the default argument. I replaced it with None, although if you would like to accept this value as the argument of appendEmp(), you can replace it with something else.
More on dangers of using mutable default arguments you can read here: Hidden features of Python: Dangers of mutable default arguments
Ps. You may also wish to do the same for __init__() method.
A mutable default argument can cause trouble if you aren't fully aware of how they work. Most beginners don't realize that the default value for a keyword argument is computed exactly once, when the definition is processed. If the function code modifies this object thereafter then the next call to use the default object will see the modified value.
I explain this with pictures starting at http://holdenweb.com/files/IntroNotes.pdf#page=118
These notes, by the way, are the draft of an open source release of my "Introduction to Python" class, so I'd appreciate any feedback.

Python keyword arguments

I have several layers of function calls, passing around a common dictionary of key word arguments:
def func1(**qwargs):
func2(**qwargs)
func3(**qwargs)
I would like to supply some default arguments in some of the subsequent function calls, something like this:
def func1(**qwargs):
func2(arg = qwargs.get("arg", default), **qwargs)
func3(**qwargs)
The problem with this approach is that if arg is inside qwargs, a TypeError is raised with "got multiple values for keyword argument".
I don't want to set qwargs["arg"] to default, because then func3 gets this argument without warrant. I could make a copy.copy of the qwargs and set "arg" in the copy, but qwargs could have large data structures in it and I don't want to copy them (maybe copy.copy wouldn't, only copy.deepcopy?).
What's the pythonic thing to do here?
Just build and use another dict for the purpose of calling func2, leaving the original alone for the later call to func3:
def func1(**qwargs):
d = dict(arg=default)
d.update(qwqargs)
func2(**d)
func3(**qwargs)
This is if you want a setting for arg in qwargs to override the default. Otherwise (if you want default to override any possible setting for arg in qwargs):
def func1(**qwargs):
d = dict(qwargs, arg=default)
func2(**d)
func3(**qwargs)
since the keyword-argument to dict overrides the value in the positional argument, if any.
To create a new dict with the same keys and values you can use
newdict=dict(qwargs)
If qwargs doesn't contain really many keys that's cheap.
If it's possible you could rewrite the functions to take their args really as dict instead of multiple args.

Categories