-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfunctions.py
More file actions
423 lines (326 loc) · 17.5 KB
/
Copy pathfunctions.py
File metadata and controls
423 lines (326 loc) · 17.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
"""Learning functions via Python Crash Course 2nd Edition"""
import os
def clear():
"""Clear terminal text."""
os.system('clear')
clear()
def greet_user(username):
"""Same thing but different."""
print(f"Hello, {username.title()}")
greet_user('bob')
# People sometimes use arguments and parameters interchangeably. Don't be surprised if you see
# the variables in a function definition referred to as arguments or the variables in a function
# call referred to as parameters.
# DISPLAY PRACTICE
def display_message():
"""help"""
print("In this chapter, I'm going to learn all about functions.")
display_message()
# FAVORITE BOOK PRACTICE
def favorite_book(title):
"""help"""
print(f"My favorite book is {title.title()}.")
favorite_book('ghost in the wires')
# Because a function definition can have multiple parameters, a function call may need
# multiple arguments. You can pass arguments to your functions in a number of ways. You
# can use positional arguments, which need to be in the same order the parameters were
# written in; keyword arguments, where each argument consists of a variable name and a
# value, and lists and dictionaries of values.
# When you call a function, Python matches each argument in the function call with a parameter
# in the function definition. The simplest way to do this is based on the order of the
# arguments provided. Values matched up this way are called positional arguments.
def describe_pet(animal_type, pet_name):
"""Display info about a pet."""
print(f"\nI have a {animal_type} named {pet_name.title()}.")
# If you mix up the order of the arguments in a function call, you get weird and unwanted results;
# don't do it.
# A keyword argument is a name-value pair that you pass to a function. You directly associate the
# name and the value within the argument, so when you pass the argument to the function, there's
# no confusion (you won't end up with a harry named Hamster). Keyword arguments free you from
# having to worry about correctly ordering your arguments in the function call, and then clarify
# the role of each value in the function call.
describe_pet(animal_type='hamster', pet_name='harry')
# When you use keyword arguments, be sure to use the exact names of the parameters in the function's
# definition.
# When writing a function, you can define a default value for each parameter. If an argument for a
# parameter is provided in the function call, Python uses the argument value. If not, it uses the
# parameters default value. So when you define a default value for a parameter, you can exclude the
# corresponding argument you'd usually write in your function call. Using default values can
# simplify your function calls and clarify the ways in which your functions are typically used.
# def describe_pet(pet_name, animal_type='dog'):
# ""docline"""
# code here
# describe_pet(pet_name='willie') # calling the function
# Note that the order of the parameters in the function definiton had to be changed. Because the
# default value makes it unnecessary to specify a type of animal as an argument, the only argument
# left in the function call is the pet's name. Python still interprets this as a positional
# argument, so if the function is called with just the pet's name, that argument will match up with
# the first parameter listed in the function's definition. This is the reason the first parameter
# needs to be pet_name.
# describe_pet('willie') # simplest way to call the function now, since the animal is a dog.
# Because no argument is provided for animal_type, Python uses the default value 'dog'.
# To describe an animal that's not a dog, you could use a function call like this:
# (NOTE: and because an explicit argument for animal_type is provided, Python will ignore the
# default value)
describe_pet(pet_name='harry', animal_type='hamster')
# NOTE: When you use default values, any parameter with a default value needs to be listed
# after all the parameters that don't have default values. This allows Python to continue
# to interpreting positional arguments correctly.
# Because postional arguments, keyword arguments, and default values can all be used
# together, often you'll have several equivalent ways to call a function.
# Consider the following definition for describe_pet() with one default value provided:
# def describe_pet(pet_name, animal_type='dog'):
# With the above definition, an argument always needs to be provided for pet_name, and this
# value can be provided using the positional or keyword format. If the animal being described
# is not a dog, an argument for animal_type must be included in the call, and this argument can
# also be specified using the positional or keyword format.
# Don't provide too many or too little arguments for a function call, or you will get an error.
# T-SHIRT PRACTICE
# def make_shirt(size, text):
# """Prints a shirt size and the text on the shirt."""
# print(f'I have a size {size} t-shirt that has "{text}" written on it.')
# make_shirt('adult small', 'I hack everything')
# make_shirt(size='adult small', text='I hack everything')
# LARGE SHIRTS PRACTICE
def make_shirt(size='large', text='I love Python'):
"""Prints a shirt size and the text on the shirt."""
print(f'I have a size {size} t-shirt that has "{text}" written on it.')
make_shirt()
make_shirt(size='medium')
make_shirt(size='adult small', text='Hi there!')
# CITIES PRACTICE
def describe_city(city_name, country='mars'):
"""Prints the name of a city and the country it's in."""
print(f"{city_name.title()} is in {country.title()}.")
describe_city('tokyo')
describe_city(country='spain', city_name='san francisco')
describe_city('hong kong', 'iceland')
# A function doesn't always have to display its output directly. Instead, it can
# process some data and then return a set of values. The value the function returns
# is called a return value. The return statement takes a value from inside a
# function and sends it back to the line that called the function. Return values let
# you to move much of your program's grunt work into functions, which can simplify
# the body of your program.
# def get_formatted_name(first_name, last_name):
# """Return a full name, neatly formatted."""
# full_name = f"{first_name} {last_name}"
# return full_name.title()
# musician = get_formatted_name('jimi', 'hendrix')
# print(f"\n{musician}")
# While this may seem like a lot of work when we could have just printed Jimi Hendrix,
# but when you consider working with a large program that needs to store many first and
# last names seperately, functions like get_formatted_name() become very useful. You
# store first and last names seperately and then call this function whenever you want
# to display a full name.
# Sometimes it makes sense to make an argument optional so that people using the function
# can choose to provide extra info only if they want to. You can use default values to
# make an argument optional.
# We can do something like this:
def get_formatted_name(first_name, last_name, middle_name=''):
"""Return a full name, neatly formatted."""
if middle_name:
full_name = f"{first_name} {middle_name} {last_name}"
else:
full_name = f"{first_name} {last_name}"
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(f"\n{musician}")
musician = get_formatted_name('john', 'lee', 'hooker')
print(f"\n{musician}")
# In the function above, we have to make middle_name the last in the definition.
# NOTE: Python interprets non-empty strings as true.
# A function can return any kind of value you need it to, including more complicated
# data structures like lists and dicionaries.
# NOTE: the value None is special, and is used when a variable has no specific value
# assigned to it. None is like a placeholder value. In conditional tests, None
# evaluates to False.
# You can also use functions in a while loop.
# CITY NAMES PRACTICE
def city_country(city, country):
"""Prints a city and the country it's in."""
print(f"\n{city.title()}, {country.title()}")
return f"{city.title()}, {country.title()}"
city_country('tokyo', 'japan')
# ALBUM PRACTICE
def make_album(artist_name, album_title, number_songs=None):
"""Returns a dictionary of an artist and the album they made."""
album_dict = {
'artist': artist_name.title(),
'album': album_title.title(),
}
if number_songs:
album_dict['number_songs'] = number_songs
print(album_dict)
return album_dict
make_album('TheFatRat', 'Ultimate Gaming Music')
make_album('Alan Walker', 'Gaming Music', 123)
# USER ALBUMS PRACTICE
def make_user_album(artist, title):
"""Asks user for input and uses that input to make a dictionary of artists and albums."""
make_user_album_dict = {
'artist': artist.title(),
'title': title.title(),
}
return make_user_album_dict
while True:
print("Enter quit at any time to quit.")
artist = input("Who is the artist? ")
if artist.lower() == 'quit':
break
title = input("What album did they make? ")
if title.lower() == 'quit':
break
info = make_user_album(artist, title)
print(info)
# When you pass a list to a function, the funcion gets direct access to the list.
def greet_users(names):
"""Print a simple greeting to each user in the list."""
for name in names:
msg = f"Hello, {name.title()}!"
print(msg)
usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)
# When we pass a list to a function, the function can modify the list. Any changes made to
# the list inside the function's body are permanent, allowing you to work efficiently even
# when you're dealing with large amounts of data.
# Start with some designs that need to be printed.
def print_models(unprinted_designs, completed_models):
"""
Simulate printing each design, until none are left.
Move each design to completed_models after printing.
"""
while unprinted_designs:
current_design = unprinted_designs.pop()
# Simulate creating a 3D print from the design.
print(f"Printing model: {current_design}")
completed_models.append(current_design)
def show_completed_models(completed_models):
"""Show all the models that were printed."""
print("\nThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)
unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
# Sometimes you'll want to prevent a function from modifying a list. You can fix this by sending
# a copy of the list to the function instead of the original list. Only the copy of the list will
# be affected.
# You can send a copy of a list to a function like this:
# function_name(list_name[:])
# The slice notation [:] makes a copy of the list to send to the function.
# If we didn't want to empty the list of unprinted designs, we could call print_models() like this:
# print_models(unprinted_designs[:], completed_models)
# MESSAGES PRACTICE
def show_messages(messages):
"""Prints text messages passed to the function."""
for message in messages:
print(message)
def send_messages(messages, sent_messages):
"""Prints text messages and change the list it's in from unsent to sent."""
while messages:
current_message = messages.pop()
print(current_message)
sent_messages.append(current_message)
unsent_messages = ['hello', 'have fun', 'bye']
sent_messages = []
show_messages(unsent_messages)
send_messages(unsent_messages[:], sent_messages)
print(unsent_messages)
print(sent_messages)
# Sometimes, you won't know how many arguments a funcion will take. Fortunately, Python
# allows for a function to collect an arbitrary number of arguments from the calling statement.
def make_pizza(*toppings):
"""Print all of the requested toppings."""
print("This pizza has:")
for topping in toppings:
print(f"- {topping}")
make_pizza('pepperoni')
make_pizza('mushrooms', 'green pepper', 'extra cheese')
# The asterisk in the parameter name *toppings tells Python to make an empty tuple called toppings
# and pack whatever value it receives into this tuple. Note that Python packs the arguments into a
# tuple, even if there is only one argument given. This * syntax works no matter how many arguments
# are received. However, the parameter that accepts an arbitrary number of arguments must be the
# last one in the function definition. Python matches positional and keyword arguments first and
# then collects any remaining arguments in the final parameter.
# NOTE: You'll often see the generic parameter name *args, which collects arbitrary positional
# arguments like this.
# Sometimes, you'll want to accept an arbitrary number of arguments, but you won't know what kind
# of info will be passed to the function. In this case, you can write functions that accept as
# many key-value pairs as the calling statement provides.
def build_profile(first, last, **user_info):
"""Build a dictionary containing everything we know about a user."""
user_info['first_name'] = first
user_info['last_name'] = last
return user_info
user_profile = build_profile('albert', 'einstein', location='princeton', field='physics')
print(user_profile)
# The double asterisks before the parameter **user_info cause Python to create an empty dictionary
# called user_info and pack whatever key-value pairs it receives into this dictionary. Within the
# function, you can access the key-value pairs in user_info just as you would for any dictionary.
# NOTE: You'll often see the parameter name **kwargs used to collect non-specific keyword arguments.
# SANDWICHES PRACTICE
def print_sandwiches(*toppings):
"""Collects the requested toppings and prints them."""
print("The sandwich has:")
for topping in toppings:
print(f"- {topping}")
# CARS PRACTICE
def list_car_details(manafacturer, model_name, **car_info):
"""Takes info about a car and stores it in a dictionary."""
car_info['manafacturer'] = manafacturer
car_info['model_name'] = model_name
return car_info
car_profile = list_car_details('subaru', 'outback', color='blue', tow_package=True)
print(car_profile)
# One advantage of functions is the way they seperate blocks of code from your main program.
# By using descriptive names for your functions, your main program will be much easier to follow.
# You can go a step farther by storing your functions in a seperate file called a module and then
# importing that file into your main program. An import statement tell Python to make the code in a
# module available in the currently running program file. Storing your functions in a seperate file
# allows you to hide the details of your program's code and focus on it's higher-level logic. It
# also allows you to reuse functions in many different programs. When you store your functions in
# seperate files, you can share those files with other programmers without having to share your
# entire program.
# NOTE: To start importing functions, you have to have a module (a file that ends in '.py'). It
# contains the code you want to import into your program.
# If you have a file called pizza.py, for example, you would add the following line of code:
# import pizza
# and then you get all of the functions from the file pizza.py in the program the import statement
# was written in. You don't actually see code being copied because Python copies the code behind
# the scenes right before the program runs.
# NOTE: To call a function imported from a module, use this syntax:
# module_name.function_name.py
# You can also import specific functions using this syntax:
# from module_name import function_name
# NOTE: You can import as many functions as you want by seperating them with commas
# from module_name import func1, func2, func3
# NOTE: With this syntax, you don't need to use dot notation when you call a function. We can call
# it by name because we've explicitly imported the function by name.
# If the name of a function you're importing might have the same name as an existing function in
# your program or if the name is long, you can use an alias when you're importing the function.
# For example:
# from pizza import make_pizza as mp
# That would rename the make_pizza function as mp.
# The general syntax for providing an alias is:
# from module_name import function_name as fn
# You can also provide an alias for a module name.
# The syntax for this is:
# import module_name as mn
# You can tell Python to import every function in a module by using the * operator.
# from pizza import *
# This asterisk tells Python to copy every function from the module pizza into the program file.
# Because every function is imported, you can call each function by name without using dot notation.
# However, it's best not to do that when you're working with larger modules that you didn't write.
# The best approach is to import the functions you want, or import the entire module and use dot
# notation.
# When you're creating functions, they should have descriptive names and be in snake_case. Same goes
# for module names. Every function should also have a docstring.
# If you specifiy a default value for a parameter, no spaces should be used on either side of the
# equal sign. The same goes for keyword arguments in function calls.
# If a function definition goes over the character limit, then you should make it look like this:
# def function_name(
# parameter_0, parameter_1, parameter_2,
# parameter_3, parameter_4, parameter_5):
# function body...
# All import statements should be written at the top of the file, right below the file docstring.