1
1
# Advanced things with functions
2
2
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
5
5
that have the same name. Like this:
6
6
7
7
``` py
@@ -13,12 +13,12 @@ def print_box(message, border='*'):
13
13
print_box(" hello" )
14
14
```
15
15
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
17
17
functions and how they are useful.
18
18
19
19
## Multiple return values
20
20
21
- Function can take multiple arguments, but they can only return one
21
+ Function can take multiple arguments, but they can only return one
22
22
value. But sometimes it makes sense to remove multiple values as well:
23
23
24
24
``` py
@@ -28,22 +28,22 @@ def login():
28
28
# how the heck are we going to return these?
29
29
```
30
30
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:
33
33
34
34
``` py
35
35
def login ():
36
36
...
37
37
return username, password
38
38
39
39
40
- username, password = login():
40
+ username, password = login()
41
41
...
42
42
```
43
43
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
47
47
class] ( ../basics/classes.md ) instead and store the values like
48
48
` self.thing = stuff ` .
49
49
@@ -56,58 +56,58 @@ def thing(*args, **kwargs):
56
56
...
57
57
```
58
58
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
60
60
function that takes ` *args ` and prints it.
61
61
62
62
``` py
63
63
>> > def thing (* args ):
64
64
... print (" now args is" , args)
65
- ...
65
+ ...
66
66
>> > thing()
67
67
now args is ()
68
68
>> > thing(1 , 2 , 3 )
69
69
now args is (1 , 2 , 3 )
70
- >> >
70
+ >> >
71
71
```
72
72
73
73
So 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
77
77
a tuple, and never raises errors. Of course, we could also use whatever
78
78
variable name we wanted instead of ` args ` .
79
79
80
- Our function with just ` *args ` takes no keyword arguments:
80
+ Our function with nothing but ` *args ` takes no keyword arguments:
81
81
82
82
``` py
83
83
>> > thing(a = 1 )
84
84
Traceback (most recent call last):
85
85
File " <stdin>" , line 1 , in < module>
86
86
TypeError : thing() got an unexpected keyword argument ' a'
87
- >> >
87
+ >> >
88
88
```
89
89
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
93
93
work.
94
94
95
95
``` py
96
96
>> > stuff = [' hello' , ' world' , ' test' ]
97
97
>> > print (* stuff)
98
98
hello world test
99
- >> >
99
+ >> >
100
100
```
101
101
102
102
## \*\* kwargs
103
103
104
- ` **kwargs ` is the same thing as ` *args ` , but with keyword arguments
104
+ ` **kwargs ` is the same thing as ` *args ` , but with keyword arguments
105
105
instead of positional arguments.
106
106
107
107
``` py
108
108
>> > def thing (** kwargs ):
109
109
... print (' now kwargs is' , kwargs)
110
- ...
110
+ ...
111
111
>> > thing(a = 1 , b = 2 )
112
112
now kwargs is {' b' : 2 , ' a' : 1 }
113
113
>> > thing(1 , 2 )
@@ -118,49 +118,50 @@ TypeError: thing() takes 0 positional arguments but 2 were given
118
118
... print (border * len (message))
119
119
... print (message)
120
120
... print (border * len (message))
121
- ...
121
+ ...
122
122
>> > kwargs = {' message' : " Hello World!" , ' border' : ' -' }
123
123
>> > print_box(** kwargs)
124
124
------------
125
125
Hello World!
126
126
------------
127
- >> >
127
+ >> >
128
128
```
129
129
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
131
131
combine ` *args ` and ` **kwargs ` easily:
132
132
133
133
``` py
134
134
>> > def thing (* args , ** kwargs ):
135
135
... print (" now args is" , args, " and kwargs is" , kwargs)
136
- ...
136
+ ...
137
137
>> > thing(1 , 2 , a = 3 , b = 4 )
138
138
now args is (1 , 2 ) and kwargs is {' b' : 4 , ' a' : 3 }
139
- >> >
139
+ >> >
140
140
```
141
141
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"
143
143
that represents it. We'll find uses for this later.
144
144
145
145
``` py
146
146
>> > def fake_print (* args , ** kwargs ):
147
147
... print (* args, ** kwargs)
148
- ...
148
+ ...
149
149
>> > print (' this' , ' is' , ' a' , ' test' , sep = ' -' )
150
150
this- is - a- test
151
151
>> > fake_print(' this' , ' is' , ' a' , ' test' , sep = ' -' )
152
152
this- is - a- test
153
- >> >
153
+ >> >
154
154
```
155
155
156
156
## Keyword-only arguments
157
157
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:
164
165
165
166
``` py
166
167
def move (source , destination , overwrite = False , backup = False ):
@@ -179,61 +180,86 @@ moving file1.txt to file2.txt
179
180
>> > move(' file1.txt' , ' file2.txt' , overwrite = True )
180
181
deleting file2.txt
181
182
moving file1.txt to file2.txt
182
- >> >
183
+ >> >
183
184
```
184
185
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
186
187
filenames, bad things will happen:
187
188
188
189
``` py
189
190
>> > move(' file1.txt' , ' file2.txt' , ' file3.txt' )
190
191
deleting file2.txt
191
192
moving file1.txt to file2.txt
192
- >> >
193
+ >> >
193
194
```
194
195
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
196
197
original ` file2.txt ` !
197
198
198
199
The problem was that now ` overwrite ` was ` 'file2.txt' ` , and the
199
200
` if overwrite ` part [ treated the string as
200
201
True] ( ../basics/what-is-true.md ) and deleted the file. That's not nice.
201
202
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
203
204
` backup ` are keyword-only:
204
205
205
206
``` py
206
207
def move (source , destination , * , overwrite = False , backup = False ):
207
208
...
208
209
```
209
210
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
+ ```
212
224
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.
218
230
219
231
## When should we use these things?
220
232
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
224
239
that we'll get an error if the function gets an invalid argument.
225
240
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 `
228
243
and ` **kwargs ` , and there's no need to avoid them.
229
244
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)`.
237
263
238
264
***
239
265
0 commit comments