diff options
Diffstat (limited to 'python.html.markdown')
| -rw-r--r-- | python.html.markdown | 190 | 
1 files changed, 124 insertions, 66 deletions
| diff --git a/python.html.markdown b/python.html.markdown index 687529b5..9d97e64d 100644 --- a/python.html.markdown +++ b/python.html.markdown @@ -10,14 +10,17 @@ contributors:      - ["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 @@ -82,7 +85,7 @@ 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 @@ -97,8 +100,9 @@ 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 (&,|) +# 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 @@ -152,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 @@ -181,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 @@ -222,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] @@ -294,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 [] @@ -348,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. @@ -454,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 @@ -482,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") @@ -497,19 +501,20 @@ with open("myfile.txt") as f:  # Writing to a file  contents = {"aa": 12, "bb": 21} -with open("myfile1.txt", "w+") as file: +with open("myfile1.txt", "w") as file:      file.write(str(contents))        # writes a string to a file -with open("myfile2.txt", "w+") as file: +import json +with open("myfile2.txt", "w") as file:      file.write(json.dumps(contents)) # writes an object to a file  # Reading from a file -with open('myfile1.txt', "r+") as file: +with open('myfile1.txt', "r") as file:      contents = file.read()           # reads a string from a file  print(contents)  # print: {"aa": 12, "bb": 21} -with open('myfile2.txt', "r+") as file: +with open('myfile2.txt', "r") as file:      contents = json.load(file)       # reads a json object from a file  print(contents)  # print: {"aa": 12, "bb": 21} @@ -521,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: @@ -533,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! @@ -549,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 @@ -596,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): @@ -611,7 +618,7 @@ 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.  # global scope  x = 5 @@ -648,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 @@ -659,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] @@ -678,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 @@ -719,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): @@ -774,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" @@ -811,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" @@ -869,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'>) @@ -936,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) @@ -951,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'>, @@ -1009,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 -## Ready For More? +from functools import wraps + +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) | 
