summaryrefslogtreecommitdiffhomepage
path: root/python.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'python.html.markdown')
-rw-r--r--python.html.markdown217
1 files changed, 144 insertions, 73 deletions
diff --git a/python.html.markdown b/python.html.markdown
index e46d726e..9d97e64d 100644
--- a/python.html.markdown
+++ b/python.html.markdown
@@ -9,14 +9,18 @@ contributors:
- ["Rommel Martinez", "https://ebzzry.io"]
- ["Roberto Fernandez Diaz", "https://github.com/robertofd1995"]
- ["caminsha", "https://github.com/caminsha"]
+ - ["Stanislav Modrak", "https://stanislav.gq"]
+ - ["John Paul Wohlscheid", "https://gitpi.us"]
filename: learnpython.py
---
-Python was created by Guido van Rossum in the early 90s. It is now one of the most popular
-languages in existence. I fell in love with Python for its syntactic clarity. It's basically
-executable pseudocode.
+Python was created by Guido van Rossum in the early 90s. It is now one of the
+most popular languages in existence. I fell in love with Python for its
+syntactic clarity. It's basically executable pseudocode.
-Note: This article applies to Python 3 specifically. Check out [here](http://learnxinyminutes.com/docs/pythonlegacy/) if you want to learn the old Python 2.7
+Note: This article applies to Python 3 specifically. Check out
+[here](http://learnxinyminutes.com/docs/pythonlegacy/) if you want to learn the
+old Python 2.7
```python
@@ -81,16 +85,29 @@ False - 5 # => -5
# Comparison operators look at the numerical value of True and False
0 == False # => True
-1 == True # => True
+2 > True # => True
2 == True # => False
-5 != False # => True
-# Using boolean logical operators on ints casts them to booleans for evaluation, but their non-cast value is returned
-# Don't mix up with bool(ints) and bitwise and/or (&,|)
+# None, 0, and empty strings/lists/dicts/tuples/sets all evaluate to False.
+# All other values are True
bool(0) # => False
+bool("") # => False
+bool([]) # => False
+bool({}) # => False
+bool(()) # => False
+bool(set()) # => False
bool(4) # => True
bool(-6) # => True
+
+# Using boolean logical operators on ints casts them to booleans for evaluation,
+# but their non-cast value is returned. Don't mix up with bool(ints) and bitwise
+# and/or (&,|)
+bool(0) # => False
+bool(2) # => True
0 and 2 # => 0
+bool(-5) # => True
+bool(2) # => True
-5 or 0 # => -5
# Equality is ==
@@ -139,10 +156,10 @@ b == a # => True, a's and b's objects are equal
# You can find the length of a string
len("This is a string") # => 16
-# You can also format using f-strings or formatted string literals (in Python 3.6+)
+# Since Python 3.6, you can use f-strings or formatted string literals.
name = "Reiko"
f"She said her name is {name}." # => "She said her name is Reiko"
-# You can basically put any Python expression inside the braces and it will be output in the string.
+# Any valid Python expression inside these braces is returned to the string.
f"{name} is {len(name)} characters long." # => "Reiko is 5 characters long."
# None is an object
@@ -153,14 +170,6 @@ None # => None
"etc" is None # => False
None is None # => True
-# None, 0, and empty strings/lists/dicts/tuples all evaluate to False.
-# All other values are True
-bool(0) # => False
-bool("") # => False
-bool([]) # => False
-bool({}) # => False
-bool(()) # => False
-
####################################################
## 2. Variables and Collections
####################################################
@@ -176,7 +185,7 @@ print("Hello, World", end="!") # => Hello, World!
input_string_var = input("Enter some data: ") # Returns the data as a string
# There are no declarations, only assignments.
-# Convention is to use lower_case_with_underscores
+# Convention in naming variables is snake_case style
some_var = 5
some_var # => 5
@@ -217,7 +226,7 @@ li[4] # Raises an IndexError
li[1:3] # Return list from index 1 to 3 => [2, 4]
li[2:] # Return list starting from index 2 => [4, 3]
li[:3] # Return list from beginning until index 3 => [1, 2, 4]
-li[::2] # Return list selecting every second entry => [1, 4]
+li[::2] # Return list selecting elements with a step size of 2 => [1, 4]
li[::-1] # Return list in reverse order => [3, 4, 2, 1]
# Use any combination of these to make advanced slices
# li[start:end:step]
@@ -289,7 +298,7 @@ filled_dict = {"one": 1, "two": 2, "three": 3}
# Note keys for dictionaries have to be immutable types. This is to ensure that
# the key can be converted to a constant hash value for quick look-ups.
# Immutable types include ints, floats, strings, tuples.
-invalid_dict = {[1,2,3]: "123"} # => Raises a TypeError: unhashable type: 'list'
+invalid_dict = {[1,2,3]: "123"} # => Yield a TypeError: unhashable type: 'list'
valid_dict = {(1,2,3):[1,2,3]} # Values can be of any type, however.
# Look up values with []
@@ -343,7 +352,7 @@ del filled_dict["one"] # Removes the key "one" from filled dict
# Sets store ... well sets
empty_set = set()
-# Initialize a set with a bunch of values. Yeah, it looks a bit like a dict. Sorry.
+# Initialize a set with a bunch of values.
some_set = {1, 1, 2, 2, 3, 4} # some_set is now {1, 2, 3, 4}
# Similar to keys of a dictionary, elements of a set have to be immutable.
@@ -415,7 +424,7 @@ for animal in ["dog", "cat", "mouse"]:
"""
"range(number)" returns an iterable of numbers
-from zero to the given number
+from zero up to (but excluding) the given number
prints:
0
1
@@ -449,8 +458,7 @@ for i in range(4, 8, 2):
print(i)
"""
-To loop over a list, and retrieve both the index and the value of each item in the list
-prints:
+Loop over a list to retrieve both the index and the value of each list item:
0 dog
1 cat
2 mouse
@@ -477,10 +485,11 @@ try:
# Use "raise" to raise an error
raise IndexError("This is an index error")
except IndexError as e:
- pass # Pass is just a no-op. Usually you would do recovery here.
+ pass # Refrain from this, provide a recovery (next example).
except (TypeError, NameError):
- pass # Multiple exceptions can be handled together, if required.
-else: # Optional clause to the try/except block. Must follow all except blocks
+ pass # Multiple exceptions can be processed jointly.
+else: # Optional clause to the try/except block. Must follow
+ # all except blocks.
print("All good!") # Runs only if the code in try raises no exceptions
finally: # Execute under all circumstances
print("We can clean up resources here")
@@ -495,6 +504,7 @@ contents = {"aa": 12, "bb": 21}
with open("myfile1.txt", "w") as file:
file.write(str(contents)) # writes a string to a file
+import json
with open("myfile2.txt", "w") as file:
file.write(json.dumps(contents)) # writes an object to a file
@@ -516,7 +526,8 @@ print(contents)
filled_dict = {"one": 1, "two": 2, "three": 3}
our_iterable = filled_dict.keys()
-print(our_iterable) # => dict_keys(['one', 'two', 'three']). This is an object that implements our Iterable interface.
+print(our_iterable) # => dict_keys(['one', 'two', 'three']). This is an object
+ # that implements our Iterable interface.
# We can loop over it.
for i in our_iterable:
@@ -528,15 +539,16 @@ our_iterable[1] # Raises a TypeError
# An iterable is an object that knows how to create an iterator.
our_iterator = iter(our_iterable)
-# Our iterator is an object that can remember the state as we traverse through it.
-# We get the next object with "next()".
+# Our iterator is an object that can remember the state as we traverse through
+# it. We get the next object with "next()".
next(our_iterator) # => "one"
# It maintains state as we iterate.
next(our_iterator) # => "two"
next(our_iterator) # => "three"
-# After the iterator has returned all of its data, it raises a StopIteration exception
+# After the iterator has returned all of its data, it raises a
+# StopIteration exception
next(our_iterator) # Raises StopIteration
# We can also loop over it, in fact, "for" does this implicitly!
@@ -544,7 +556,7 @@ our_iterator = iter(our_iterable)
for i in our_iterator:
print(i) # Prints one, two, three
-# You can grab all the elements of an iterable or iterator by calling list() on it.
+# You can grab all the elements of an iterable or iterator by call of list().
list(our_iterable) # => Returns ["one", "two", "three"]
list(our_iterator) # => Returns [] because state is saved
@@ -591,12 +603,12 @@ all_the_args(1, 2, a=3, b=4) prints:
"""
# When calling functions, you can do the opposite of args/kwargs!
-# Use * to expand tuples and use ** to expand kwargs.
+# Use * to expand args (tuples) and use ** to expand kwargs (dictionaries).
args = (1, 2, 3, 4)
kwargs = {"a": 3, "b": 4}
-all_the_args(*args) # equivalent to all_the_args(1, 2, 3, 4)
-all_the_args(**kwargs) # equivalent to all_the_args(a=3, b=4)
-all_the_args(*args, **kwargs) # equivalent to all_the_args(1, 2, 3, 4, a=3, b=4)
+all_the_args(*args) # equivalent: all_the_args(1, 2, 3, 4)
+all_the_args(**kwargs) # equivalent: all_the_args(a=3, b=4)
+all_the_args(*args, **kwargs) # equivalent: all_the_args(1, 2, 3, 4, a=3, b=4)
# Returning multiple values (with tuple assignments)
def swap(x, y):
@@ -606,17 +618,19 @@ def swap(x, y):
x = 1
y = 2
x, y = swap(x, y) # => x = 2, y = 1
-# (x, y) = swap(x,y) # Again parenthesis have been excluded but can be included.
+# (x, y) = swap(x,y) # Again the use of parenthesis is optional.
-# Function Scope
+# global scope
x = 5
def set_x(num):
- # Local var x not the same as global variable x
+ # local scope begins here
+ # local var x not the same as global var x
x = num # => 43
print(x) # => 43
def set_global_x(num):
+ # global indicates that particular var lives in the global scope
global x
print(x) # => 5
x = num # global var x is now set to 6
@@ -624,6 +638,12 @@ def set_global_x(num):
set_x(43)
set_global_x(6)
+"""
+prints:
+ 43
+ 5
+ 6
+"""
# Python has first class functions
@@ -635,6 +655,22 @@ def create_adder(x):
add_10 = create_adder(10)
add_10(3) # => 13
+# Closures in nested functions:
+# We can use the nonlocal keyword to work with variables in nested scope which shouldn't be declared in the inner functions.
+def create_avg():
+ total = 0
+ count = 0
+ def avg(n):
+ nonlocal total, count
+ total += n
+ count += 1
+ return total/count
+ return avg
+avg = create_avg()
+avg(3) # => 3.0
+avg(5) # (3+5)/2 => 4.0
+avg(7) # (8+7)/3 => 5.0
+
# There are also anonymous functions
(lambda x: x > 2)(3) # => True
(lambda x, y: x ** 2 + y ** 2)(2, 1) # => 5
@@ -646,7 +682,7 @@ list(map(max, [1, 2, 3], [4, 2, 1])) # => [4, 2, 3]
list(filter(lambda x: x > 5, [3, 4, 5, 6, 7])) # => [6, 7]
# We can use list comprehensions for nice maps and filters
-# List comprehension stores the output as a list which can itself be a nested list
+# List comprehension stores the output as a list (which itself may be nested).
[add_10(i) for i in [1, 2, 3]] # => [11, 12, 13]
[x for x in [3, 4, 5, 6, 7] if x > 5] # => [6, 7]
@@ -665,8 +701,8 @@ print(math.sqrt(16)) # => 4.0
# You can get specific functions from a module
from math import ceil, floor
-print(ceil(3.7)) # => 4.0
-print(floor(3.7)) # => 3.0
+print(ceil(3.7)) # => 4
+print(floor(3.7)) # => 3
# You can import all functions from a module.
# Warning: this is not recommended
@@ -706,14 +742,16 @@ class Human:
# Note that the double leading and trailing underscores denote objects
# or attributes that are used by Python but that live in user-controlled
# namespaces. Methods(or objects or attributes) like: __init__, __str__,
- # __repr__ etc. are called special methods (or sometimes called dunder methods)
- # You should not invent such names on your own.
+ # __repr__ etc. are called special methods (or sometimes called dunder
+ # methods). You should not invent such names on your own.
def __init__(self, name):
# Assign the argument to the instance's name attribute
self.name = name
# Initialize property
- self._age = 0
+ self._age = 0 # the leading underscore indicates the "age" property is
+ # intended to be used internally
+ # do not rely on this to be enforced: it's a hint to other devs
# An instance method. All methods take "self" as the first argument
def say(self, msg):
@@ -761,7 +799,7 @@ if __name__ == '__main__':
i.say("hi") # "Ian: hi"
j = Human("Joel")
j.say("hello") # "Joel: hello"
- # i and j are instances of type Human, or in other words: they are Human objects
+ # i and j are instances of type Human; i.e., they are Human objects.
# Call our class method
i.say(i.get_species()) # "Ian: H. sapiens"
@@ -798,8 +836,8 @@ if __name__ == '__main__':
# "species", "name", and "age", as well as methods, like "sing" and "grunt"
# from the Human class, but can also have its own unique properties.
-# To take advantage of modularization by file you could place the classes above in their own files,
-# say, human.py
+# To take advantage of modularization by file you could place the classes above
+# in their own files, say, human.py
# To import functions from other files use the following format
# from "filename-without-extension" import "function-or-class"
@@ -856,7 +894,8 @@ if __name__ == '__main__':
if type(sup) is Superhero:
print('I am a superhero')
- # Get the Method Resolution search Order used by both getattr() and super()
+ # Get the "Method Resolution Order" used by both getattr() and super()
+ # (the order in which classes are searched for an attribute or method)
# This attribute is dynamic and can be updated
print(Superhero.__mro__) # => (<class '__main__.Superhero'>,
# => <class 'human.Human'>, <class 'object'>)
@@ -923,8 +962,8 @@ class Batman(Superhero, Bat):
# However we are dealing with multiple inheritance here, and super()
# only works with the next base class in the MRO list.
# So instead we explicitly call __init__ for all ancestors.
- # The use of *args and **kwargs allows for a clean way to pass arguments,
- # with each parent "peeling a layer of the onion".
+ # The use of *args and **kwargs allows for a clean way to pass
+ # arguments, with each parent "peeling a layer of the onion".
Superhero.__init__(self, 'anonymous', movie=True,
superpowers=['Wealthy'], *args, **kwargs)
Bat.__init__(self, *args, can_fly=False, **kwargs)
@@ -938,8 +977,7 @@ class Batman(Superhero, Bat):
if __name__ == '__main__':
sup = Batman()
- # Get the Method Resolution search Order used by both getattr() and super().
- # This attribute is dynamic and can be updated
+ # The Method Resolution Order
print(Batman.__mro__) # => (<class '__main__.Batman'>,
# => <class 'superhero.Superhero'>,
# => <class 'human.Human'>,
@@ -996,39 +1034,72 @@ gen_to_list = list(values)
print(gen_to_list) # => [-1, -2, -3, -4, -5]
-# Decorators
-# In this example `beg` wraps `say`. If say_please is True then it
-# will change the returned message.
-from functools import wraps
+# Decorators are a form of syntactic sugar.
+# They make code easier to read while accomplishing clunky syntax.
+# Wrappers are one type of decorator.
+# They're really useful for adding logging to existing functions without needing to modify them.
-def beg(target_function):
- @wraps(target_function)
+def log_function(func):
def wrapper(*args, **kwargs):
- msg, say_please = target_function(*args, **kwargs)
- if say_please:
- return "{} {}".format(msg, "Please! I am poor :(")
- return msg
-
+ print("Entering function", func.__name__)
+ result = func(*args, **kwargs)
+ print("Exiting function", func.__name__)
+ return result
return wrapper
+@log_function # equivalent:
+def my_function(x,y): # def my_function(x,y):
+ return x+y # return x+y
+ # my_function = log_function(my_function)
+# The decorator @log_function tells us as we begin reading the function definition
+# for my_function that this function will be wrapped with log_function.
+# When function definitions are long, it can be hard to parse the non-decorated
+# assignment at the end of the definition.
-@beg
-def say(say_please=False):
- msg = "Can you buy me a beer?"
- return msg, say_please
+my_function(1,2) # => "Entering function my_function"
+ # => "3"
+ # => "Exiting function my_function"
+# But there's a problem.
+# What happens if we try to get some information about my_function?
-print(say()) # Can you buy me a beer?
-print(say(say_please=True)) # Can you buy me a beer? Please! I am poor :(
-```
+print(my_function.__name__) # => 'wrapper'
+print(my_function.__code__.co_argcount) # => 0. The argcount is 0 because both arguments in wrapper()'s signature are optional.
+
+# Because our decorator is equivalent to my_function = log_function(my_function)
+# we've replaced information about my_function with information from wrapper
+
+# Fix this using functools
+
+from functools import wraps
-## Ready For More?
+def log_function(func):
+ @wraps(func) # this ensures docstring, function name, arguments list, etc. are all copied
+ # to the wrapped function - instead of being replaced with wrapper's info
+ def wrapper(*args, **kwargs):
+ print("Entering function", func.__name__)
+ result = func(*args, **kwargs)
+ print("Exiting function", func.__name__)
+ return result
+ return wrapper
+
+@log_function
+def my_function(x,y):
+ return x+y
+
+my_function(1,2) # => "Entering function my_function"
+ # => "3"
+ # => "Exiting function my_function"
+
+print(my_function.__name__) # => 'my_function'
+print(my_function.__code__.co_argcount) # => 2
+
+```
### Free Online
* [Automate the Boring Stuff with Python](https://automatetheboringstuff.com)
-* [Ideas for Python Projects](http://pythonpracticeprojects.com)
* [The Official Docs](https://docs.python.org/3/)
* [Hitchhiker's Guide to Python](https://docs.python-guide.org/en/latest/)
* [Python Course](https://www.python-course.eu)