11# Advanced things with functions
22
3- Now we know [ how to define functions] ( ../basics/defining-functions.md ) .
4- Functions can take arguments, and they will end up with local variables
3+ Now we know [ how to define functions] ( ../basics/defining-functions.md ) .
4+ Functions can take arguments, and they will end up with local variables
55that have the same name. Like this:
66
77``` py
@@ -13,12 +13,12 @@ def print_box(message, border='*'):
1313print_box(" hello" )
1414```
1515
16- In this chapter we'll learn more things we can do with defining
16+ In this chapter we'll learn more things we can do with defining
1717functions and how they are useful.
1818
1919## Multiple return values
2020
21- Function can take multiple arguments, but they can only return one
21+ Function can take multiple arguments, but they can only return one
2222value. But sometimes it makes sense to remove multiple values as well:
2323
2424``` py
@@ -28,22 +28,22 @@ def login():
2828 # how the heck are we going to return these?
2929```
3030
31- The best solution is to just return a tuple of values, and just unpack
32- that wherever the function is called:
31+ The best solution is to return a tuple of values, and just unpack that
32+ wherever the function is called:
3333
3434``` py
3535def login ():
3636 ...
3737 return username, password
3838
3939
40- username, password = login():
40+ username, password = login()
4141...
4242```
4343
44- That gets kind of messy if there are more than three values to return,
45- but I have never needed to return more than three values. If you think
46- you need to return four or more values you probably want to use [ a
44+ That gets kind of messy if there are more than three values to return,
45+ but I have never needed to return more than three values. If you think
46+ you need to return four or more values you probably want to use [ a
4747class] ( ../basics/classes.md ) instead and store the values like
4848` self.thing = stuff ` .
4949
@@ -56,58 +56,58 @@ def thing(*args, **kwargs):
5656 ...
5757```
5858
59- Functions like this are actually quite easy to understand. Let's make a
59+ Functions like this are actually quite easy to understand. Let's make a
6060function that takes ` *args ` and prints it.
6161
6262``` py
6363>> > def thing (* args ):
6464... print (" now args is" , args)
65- ...
65+ ...
6666>> > thing()
6767now args is ()
6868>> > thing(1 , 2 , 3 )
6969now args is (1 , 2 , 3 )
70- >> >
70+ >> >
7171```
7272
7373So far we have learned that if we want to call a function like
74- ` thing(1, 2, 3) ` , then we need to define the arguments when defining the
75- function like ` def thing(a, b, c) ` . But ` *args ` just magically gets
76- whatever positional arguments the function is given and turns them into
74+ ` thing(1, 2, 3) ` , then we need to define the arguments when defining the
75+ function like ` def thing(a, b, c) ` . But ` *args ` just magically gets
76+ whatever positional arguments the function is given and turns them into
7777a tuple, and never raises errors. Of course, we could also use whatever
7878variable name we wanted instead of ` args ` .
7979
80- Our function with just ` *args ` takes no keyword arguments:
80+ Our function with nothing but ` *args ` takes no keyword arguments:
8181
8282``` py
8383>> > thing(a = 1 )
8484Traceback (most recent call last):
8585 File " <stdin>" , line 1 , in < module>
8686TypeError : thing() got an unexpected keyword argument ' a'
87- >> >
87+ >> >
8888```
8989
90- We can also save our arguments to a variable as a list, and then pass
91- them to a function by adding a ` * ` . Actually it doesn't need to be a
92- list or a tuple, anything [ iterable] ( ../basics/loops.md#summary ) will
90+ We can also save our arguments to a variable as a list, and then pass
91+ them to a function by adding a ` * ` . Actually it doesn't need to be a
92+ list or a tuple, anything [ iterable] ( ../basics/loops.md#summary ) will
9393work.
9494
9595``` py
9696>> > stuff = [' hello' , ' world' , ' test' ]
9797>> > print (* stuff)
9898hello world test
99- >> >
99+ >> >
100100```
101101
102102## \*\* kwargs
103103
104- ` **kwargs ` is the same thing as ` *args ` , but with keyword arguments
104+ ` **kwargs ` is the same thing as ` *args ` , but with keyword arguments
105105instead of positional arguments.
106106
107107``` py
108108>> > def thing (** kwargs ):
109109... print (' now kwargs is' , kwargs)
110- ...
110+ ...
111111>> > thing(a = 1 , b = 2 )
112112now kwargs is {' b' : 2 , ' a' : 1 }
113113>> > thing(1 , 2 )
@@ -118,49 +118,50 @@ TypeError: thing() takes 0 positional arguments but 2 were given
118118... print (border * len (message))
119119... print (message)
120120... print (border * len (message))
121- ...
121+ ...
122122>> > kwargs = {' message' : " Hello World!" , ' border' : ' -' }
123123>> > print_box(** kwargs)
124124------------
125125Hello World!
126126------------
127- >> >
127+ >> >
128128```
129129
130- Sometimes it's handy to capture all arguments our function takes. We can
130+ Sometimes it's handy to capture all arguments our function takes. We can
131131combine ` *args ` and ` **kwargs ` easily:
132132
133133``` py
134134>> > def thing (* args , ** kwargs ):
135135... print (" now args is" , args, " and kwargs is" , kwargs)
136- ...
136+ ...
137137>> > thing(1 , 2 , a = 3 , b = 4 )
138138now args is (1 , 2 ) and kwargs is {' b' : 4 , ' a' : 3 }
139- >> >
139+ >> >
140140```
141141
142- This is often used for calling a function from another "fake function"
142+ This is often used for calling a function from another "fake function"
143143that represents it. We'll find uses for this later.
144144
145145``` py
146146>> > def fake_print (* args , ** kwargs ):
147147... print (* args, ** kwargs)
148- ...
148+ ...
149149>> > print (' this' , ' is' , ' a' , ' test' , sep = ' -' )
150150this- is - a- test
151151>> > fake_print(' this' , ' is' , ' a' , ' test' , sep = ' -' )
152152this- is - a- test
153- >> >
153+ >> >
154154```
155155
156156## Keyword-only arguments
157157
158- Let's say that we have a function that moves a file. It probably takes
159- ` source ` and ` destination ` arguments, but it might also take other
160- arguments. For example, it might take an ` overwrite ` argument that makes
161- it remove ` destination ` before copying if it exists already or a
162- ` backup ` argument that makes it do a backup of the file just in case the
163- copying fails. So our function would look like this:
158+ Let's say that we have a function that moves a file. It probably takes
159+ ` source ` and ` destination ` arguments, but it might also take other
160+ arguments that customize how it moves the file. For example, it might
161+ take an ` overwrite ` argument that makes it remove ` destination ` before
162+ moving if it exists already or a ` backup ` argument that makes it do a
163+ backup of the file just in case the moving fails. So our function would
164+ look like this:
164165
165166``` py
166167def move (source , destination , overwrite = False , backup = False ):
@@ -179,61 +180,86 @@ moving file1.txt to file2.txt
179180>> > move(' file1.txt' , ' file2.txt' , overwrite = True )
180181deleting file2.txt
181182moving file1.txt to file2.txt
182- >> >
183+ >> >
183184```
184185
185- This works just fine, but if we accidentally give the function three
186+ This works just fine, but if we accidentally give the function three
186187filenames, bad things will happen:
187188
188189``` py
189190>> > move(' file1.txt' , ' file2.txt' , ' file3.txt' )
190191deleting file2.txt
191192moving file1.txt to file2.txt
192- >> >
193+ >> >
193194```
194195
195- Oh crap, that's not what we wanted at all. We have just lost the
196+ Oh crap, that's not what we wanted at all. We have just lost the
196197original ` file2.txt ` !
197198
198199The problem was that now ` overwrite ` was ` 'file2.txt' ` , and the
199200` if overwrite ` part [ treated the string as
200201True] ( ../basics/what-is-true.md ) and deleted the file. That's not nice.
201202
202- The solution is to change our move function so that ` overwrite ` and
203+ The solution is to change our move function so that ` overwrite ` and
203204` backup ` are keyword-only:
204205
205206``` py
206207def move (source , destination , * , overwrite = False , backup = False ):
207208 ...
208209```
209210
210- Note the ` * ` between ` destination ` and ` overwrite ` . It means that the
211- arguments after it must be specified as keyword arguments.
211+ The ` * ` between ` destination ` and ` overwrite ` means that ` overwrite ` and
212+ ` backup ` must be given as keyword arguments.
213+
214+ ``` py
215+ >> > move(' file1.txt' , ' file2.txt' , overwrite = True )
216+ deleting file2.txt
217+ moving file1.txt to file2.txt
218+ >> > move(' file1.txt' , ' file2.txt' , True )
219+ Traceback (most recent call last):
220+ File " <stdin>" , line 1 , in < module>
221+ TypeError : move() takes 2 positional arguments but 3 were given
222+ >> >
223+ ```
212224
213- Our new move function also makes it impossible to write things like
214- ` move('file1.txt', 'file2.txt', False, True) ` . The problem with calling
215- the move function like that is that nobody can guess what it does by
216- just looking at it , but it's much easier to guess what
217- ` move('file1.txt', 'file2.txt', backup =True) ` does.
225+ As you can see, our new move function also forces everyone to use it
226+ like ` move('file1.txt', 'file2.txt', overwrite= True) ` instead of
227+ ` move('file1.txt', 'file2.txt', True) ` . That's good because it's hard to
228+ guess what a positional ` True ` does , but it's easy to guess what
229+ ` overwrite =True` does.
218230
219231## When should we use these things?
220232
221- We don't need ` *args ` and ` **kwargs ` for most of the functions we write.
222- Often functions just do something and arguments are a way to change how
223- they do that, and by not taking ` *args ` or ` **kwargs ` we can make sure
233+ There's nothing wrong with returning a tuple from a function, and you
234+ are free to do that whenever you need it.
235+
236+ We don't need ` *args ` and ` **kwargs ` for most of the functions we write.
237+ Often functions just do something and arguments are a way to change how
238+ they do that, and by not taking ` *args ` or ` **kwargs ` we can make sure
224239that we'll get an error if the function gets an invalid argument.
225240
226- When we need to make something that takes whatever arguments it's given
227- or call a function with arguments that come from a list we need ` *args `
241+ When we need to make something that takes whatever arguments it's given
242+ or call a function with arguments that come from a list we need ` *args `
228243and ` **kwargs ` , and there's no need to avoid them.
229244
230- I don't recommend using keyword-only arguments with functions like our
231- ` print_box ` . It's easy enough to guess what ` print_box('hello', '-') `
232- does, and there's no need to force everyone to do
233- ` print_box('hello', border='-') ` . On the other hand, it's hard to guess
234- what ` copy('file1.txt', 'file2.txt', True, False) ` does, so using
235- keyword-only arguments makes sense and also avoids the file deleting
236- problem.
245+ I don't recommend using keyword-only arguments with functions like our
246+ ` print_box ` . It's easy enough to guess what ` print_box('hello', '-') `
247+ does, and there's no need to deny that. On the other hand, it's much
248+ harder to guess what ` move('file1.txt', 'file2.txt', True, False) ` does,
249+ so using keyword-only arguments makes sense and gets rid of the file
250+ deleting problem.
251+
252+ ## Summary
253+
254+ - If you want to return multiple values from a function you can return
255+ a tuple.
256+ - Defining a function that takes ` *args ` as an argument makes ` args ` a
257+ tuple of positional arguments. ` **kwargs ` is the same thing with
258+ dictionaries and keyword arguments.
259+ - Adding a ` * ` in a function definition makes all arguments after it
260+ keyword-only. This is useful when using positional arguments would
261+ look implicit, like the True and False in `move('file1.txt',
262+ 'file2.txt', True, False)`.
237263
238264***
239265
0 commit comments