diff --git a/src/additions/test_generators.py b/src/additions/test_generators.py index 6b2f2736..025967b6 100644 --- a/src/additions/test_generators.py +++ b/src/additions/test_generators.py @@ -31,7 +31,7 @@ def lottery(): def test_generators(): - """Yield statement""" + #Yield statement for number_index, random_number in enumerate(lottery()): if number_index < 3: assert 0 <= random_number <= 10 diff --git a/src/classes/test_class_and_instance_variables.py b/src/classes/test_class_and_instance_variables.py index d33ff2e6..1332bcac 100644 --- a/src/classes/test_class_and_instance_variables.py +++ b/src/classes/test_class_and_instance_variables.py @@ -8,9 +8,8 @@ def test_class_and_instance_variables(): - """Class and Instance Variables.""" + #Class and Instance Variables. - # pylint: disable=too-few-public-methods class Dog: """Dog class example""" kind = 'canine' # Class variable shared by all instances. @@ -31,11 +30,12 @@ def __init__(self, name): # Unique to buddy. assert buddy.name == 'Buddy' - # Shared data can have possibly surprising effects with involving mutable objects such as lists - # and dictionaries. For example, the tricks list in the following code should not be used as a - # class variable because just a single list would be shared by all Dog instances. + """ Shared data can have possibly surprising effects with involving mutable objects such as lists + and dictionaries. For example, the tricks list in the following code should not be used as a + class variable because just a single list would be shared by all Dog instances. - # pylint: disable=too-few-public-methods + pylint: disable=too-few-public-methods + """ class DogWithSharedTricks: """Dog class example with wrong shared variable usage""" tricks = [] # Mistaken use of a class variable (see below) for mutable objects. diff --git a/src/classes/test_class_definition.py b/src/classes/test_class_definition.py index 9251c60e..27fc3c6e 100644 --- a/src/classes/test_class_definition.py +++ b/src/classes/test_class_definition.py @@ -9,12 +9,13 @@ def test_class_definition(): - """Class definition.""" - - # Class definitions, like function definitions (def statements) must be executed before they - # have any effect. (You could conceivably place a class definition in a branch of an if - # statement, or inside a function.) + """Class definition. + Class definitions, like function definitions (def statements) must be executed before they + have any effect. (You could conceivably place a class definition in a branch of an if + statement, or inside a function.) + """ + class GreetingClass: """Example of the class definition @@ -23,23 +24,25 @@ class GreetingClass: name = 'user' def say_hello(self): - """Class method.""" - # The self parameter is a reference to the class itself, and is used to access variables - # that belongs to the class. It does not have to be named self , you can call it - # whatever you like, but it has to be the first parameter of any function in the class. + """Class method. + The self parameter is a reference to the class itself, and is used to access variables + that belongs to the class. It does not have to be named self , you can call it + whatever you like, but it has to be the first parameter of any function in the class. + """ return 'Hello ' + self.name def say_goodbye(self): """Class method.""" return 'Goodbye ' + self.name - # When a class definition is entered, a new namespace is created, and used as the local scope — + """ When a class definition is entered, a new namespace is created, and used as the local scope — # thus, all assignments to local variables go into this new namespace. In particular, function # definitions bind the name of the new function here. # Class instantiation uses function notation. Just pretend that the class object is a # parameterless function that returns a new instance of the class. For example the following - # code will creates a new instance of the class and assigns this object to the local variable. + # code will creates a new instance of the class and assigns this object to the local variable." + """ greeter = GreetingClass() assert greeter.say_hello() == 'Hello user' diff --git a/src/classes/test_class_objects.py b/src/classes/test_class_objects.py index 2d5a4cdb..2b471948 100644 --- a/src/classes/test_class_objects.py +++ b/src/classes/test_class_objects.py @@ -13,13 +13,13 @@ def test_class_objects(): Class objects support two kinds of operations: - attribute references - instantiation. + + ATTRIBUTE REFERENCES use the standard syntax used for all attribute references in + Python: obj.name. Valid attribute names are all the names that were in the class’s namespace + when the class object was created. For class MyCounter the following references are valid + attribute references: """ - # ATTRIBUTE REFERENCES use the standard syntax used for all attribute references in - # Python: obj.name. Valid attribute names are all the names that were in the class’s namespace - # when the class object was created. For class MyCounter the following references are valid - # attribute references: - class ComplexNumber: """Example of the complex numbers class""" diff --git a/src/classes/test_method_objects.py b/src/classes/test_method_objects.py index 1d4e66a9..4d6f6f98 100644 --- a/src/classes/test_method_objects.py +++ b/src/classes/test_method_objects.py @@ -22,19 +22,20 @@ def increment_counter(self): def test_method_objects(): - """Method Objects.""" + """Method Objects. - # The other kind of instance attribute reference is a method. A method is a function that - # “belongs to” an object. (In Python, the term method is not unique to class instances: other - # object types can have methods as well. For example, list objects have methods called append, - # insert, remove, sort, and so on. However, in the following discussion, we’ll use the term - # method exclusively to mean methods of class instance objects, unless explicitly stated - # otherwise.) + The other kind of instance attribute reference is a method. A method is a function that + “belongs to” an object. (In Python, the term method is not unique to class instances: other + object types can have methods as well. For example, list objects have methods called append, + insert, remove, sort, and so on. However, in the following discussion, we’ll use the term + method exclusively to mean methods of class instance objects, unless explicitly stated + otherwise.) - # But be aware that counter.get_counter() is not the same thing as MyCounter.get_counter() — - # it is a method object, not a function object. + But be aware that counter.get_counter() is not the same thing as MyCounter.get_counter() — + it is a method object, not a function object. - # Usually, a method is called right after it is bound + Usually, a method is called right after it is bound" + """ counter = MyCounter() assert counter.get_counter() == 10 @@ -43,18 +44,18 @@ def test_method_objects(): get_counter = counter.get_counter assert get_counter() == 10 - # What exactly happens when a method is called? You may have noticed that counter.get_counter() - # was called without an argument above, even though the function definition for get_counter() - # specified an argument (self). What happened to the argument? Surely Python raises an - # exception when a function that requires an argument is called without any — even if the - # argument isn’t actually used… - - # Actually, you may have guessed the answer: the special thing about methods is that the - # instance object is passed as the first argument of the function. In our example, the call - # counter.get_counter() is exactly equivalent to MyCounter.get_counter(counter). In general, - # calling a method with a list of n arguments is equivalent to calling the corresponding - # function with an argument list that is created by inserting the method’s instance object - # before the first argument. - + """ What exactly happens when a method is called? You may have noticed that counter.get_counter() + was called without an argument above, even though the function definition for get_counter() + specified an argument (self). What happened to the argument? Surely Python raises an + exception when a function that requires an argument is called without any — even if the + argument isn't actually used… + + Actually, you may have guessed the answer: the special thing about methods is that the + instance object is passed as the first argument of the function. In our example, the call + counter.get_counter() is exactly equivalent to MyCounter.get_counter(counter). In general, + calling a method with a list of n arguments is equivalent to calling the corresponding + function with an argument list that is created by inserting the method’s instance object + before the first argument. + """ assert counter.get_counter() == 10 assert MyCounter.get_counter(counter) == 10 diff --git a/src/control_flow/test_continue.py b/src/control_flow/test_continue.py index 23c015bd..a3d22ff6 100644 --- a/src/control_flow/test_continue.py +++ b/src/control_flow/test_continue.py @@ -7,11 +7,12 @@ def test_continue_statement(): - """CONTINUE statement in FOR loop""" + """CONTINUE statement in FOR loop # Let's # This list will contain only even numbers from the range. + """ even_numbers = [] # This list will contain every other numbers (in this case - ods). rest_of_the_numbers = [] diff --git a/src/control_flow/test_for.py b/src/control_flow/test_for.py index 7411277b..e0a63d38 100644 --- a/src/control_flow/test_for.py +++ b/src/control_flow/test_for.py @@ -124,10 +124,11 @@ def test_range_function(): assert list(range(5)) == [0, 1, 2, 3, 4] - # The given end point is never part of the generated sequence; range(10) generates 10 values, - # the legal indices for items of a sequence of length 10. It is possible to let the range start - # at another number, or to specify a different increment (even negative; sometimes this is - # called the ‘step’): + """The given end point is never part of the generated sequence; range(10) generates 10 values, + the legal indices for items of a sequence of length 10. It is possible to let the range start + at another number, or to specify a different increment (even negative; sometimes this is + called the 'step'):" + """ assert list(range(5, 10)) == [5, 6, 7, 8, 9] assert list(range(0, 10, 3)) == [0, 3, 6, 9] diff --git a/src/exceptions/test_handle_exceptions.py b/src/exceptions/test_handle_exceptions.py index 342561a3..0a593535 100644 --- a/src/exceptions/test_handle_exceptions.py +++ b/src/exceptions/test_handle_exceptions.py @@ -54,11 +54,11 @@ def test_handle_exceptions(): assert exception_has_been_handled - # A try statement may have more than one except clause, to specify handlers for different - # exceptions. At most one handler will be executed. Handlers only handle exceptions that occur - # in the corresponding try clause, not in other handlers of the same try statement. An except - # clause may name multiple exceptions as a parenthesized tuple, for example: - + """A try statement may have more than one except clause, to specify handlers for different + exceptions. At most one handler will be executed. Handlers only handle exceptions that occur + in the corresponding try clause, not in other handlers of the same try statement. An except + clause may name multiple exceptions as a parenthesized tuple, for example: + """ exception_has_been_handled = False try: result = 10 * (1 / 0) # division by zero @@ -85,10 +85,10 @@ def test_handle_exceptions(): assert exception_has_been_handled - # The try … except statement has an optional else clause, which, when present, must follow all + """ The try … except statement has an optional else clause, which, when present, must follow all # except clauses. It is useful for code that must be executed if the try clause does not raise # an exception. For example: - + """ exception_has_been_handled = False no_exceptions_has_been_fired = False diff --git a/src/functions/test_function_arbitrary_arguments.py b/src/functions/test_function_arbitrary_arguments.py index 2b00d176..2b7c33bc 100644 --- a/src/functions/test_function_arbitrary_arguments.py +++ b/src/functions/test_function_arbitrary_arguments.py @@ -8,13 +8,14 @@ def test_function_arbitrary_arguments(): - """Arbitrary Argument Lists""" - - # When a final formal parameter of the form **name is present, it receives a dictionary - # containing all keyword arguments except for those corresponding to a formal parameter. - # This may be combined with a formal parameter of the form *name which receives a tuple - # containing the positional arguments beyond the formal parameter list. - # (*name must occur before **name.) For example, if we define a function like this: + """Arbitrary Argument Lists + + When a final formal parameter of the form **name is present, it receives a dictionary + containing all keyword arguments except for those corresponding to a formal parameter. + This may be combined with a formal parameter of the form *name which receives a tuple + containing the positional arguments beyond the formal parameter list. + (*name must occur before **name.) For example, if we define a function like this: + """ def test_function(first_param, *arguments): """This function accepts its arguments through "arguments" tuple""" assert first_param == 'first param' @@ -22,10 +23,11 @@ def test_function(first_param, *arguments): test_function('first param', 'second param', 'third param') - # Normally, these variadic arguments will be last in the list of formal parameters, because - # they scoop up all remaining input arguments that are passed to the function. Any formal - # parameters which occur after the *args parameter are ‘keyword-only’ arguments, meaning that - # they can only be used as keywords rather than positional arguments. + """ Normally, these variadic arguments will be last in the list of formal parameters, because + they scoop up all remaining input arguments that are passed to the function. Any formal + parameters which occur after the *args parameter are 'keyword-only' arguments, meaning that + they can only be used as keywords rather than positional arguments. + """ def concat(*args, sep='/'): return sep.join(args) diff --git a/src/functions/test_function_decorators.py b/src/functions/test_function_decorators.py index 1603a5ff..737e4b21 100644 --- a/src/functions/test_function_decorators.py +++ b/src/functions/test_function_decorators.py @@ -11,13 +11,15 @@ def test_function_decorators(): - """Function Decorators.""" + """Function Decorators. # Function decorators are simply wrappers to existing functions. Putting the ideas mentioned # above together, we can build a decorator. In this example let's consider a function that # wraps the string output of another function by p tags. # This is the function that we want to decorate. + """ + def greeting(name): return "Hello, {0}!".format(name) @@ -32,12 +34,14 @@ def function_wrapper(name): # Here we go, we've just decorated the function output without changing the function itself. assert my_get_text('John') == '

Hello, John!

' # With decorator. - assert greeting('John') == 'Hello, John!' # Without decorator. + assert greeting('John') == 'Hello, John!' + """ Without decorator. # Now, Python makes creating and using decorators a bit cleaner and nicer for the programmer # through some syntactic sugar There is a neat shortcut for that, which is to mention the # name of the decorating function before the function to be decorated. The name of the # decorator should be prepended with an @ symbol. + """ @decorate_with_p def greeting_with_p(name): @@ -65,17 +69,17 @@ def greeting_with_div_p(name): assert greeting_with_div_p('John') == '

Hello, John!

' - # One important thing to notice here is that the order of setting our decorators matters. - # If the order was different in the example above, the output would have been different. - - # Passing arguments to decorators. + """ One important thing to notice here is that the order of setting our decorators matters. + If the order was different in the example above, the output would have been different. - # Looking back at the example before, you can notice how redundant the decorators in the - # example are. 2 decorators(decorate_with_div, decorate_with_p) each with the same - # functionality but wrapping the string with different tags. We can definitely do much better - # than that. Why not have a more general implementation for one that takes the tag to wrap - # with as a string? Yes please! + Passing arguments to decorators. + Looking back at the example before, you can notice how redundant the decorators in the + example are. 2 decorators(decorate_with_div, decorate_with_p) each with the same + functionality but wrapping the string with different tags. We can definitely do much better + than that. Why not have a more general implementation for one that takes the tag to wrap + with as a string? Yes please! + """ def tags(tag_name): def tags_decorator(func): def func_wrapper(name): diff --git a/src/functions/test_function_default_arguments.py b/src/functions/test_function_default_arguments.py index aa2a09ad..6e7255fb 100644 --- a/src/functions/test_function_default_arguments.py +++ b/src/functions/test_function_default_arguments.py @@ -16,10 +16,11 @@ def power_of(number, power=2): def test_default_function_arguments(): - """Test default function arguments""" + """Test default function arguments # This function power_of can be called in several ways because it has default value for # the second argument. First we may call it omitting the second argument at all. + """ assert power_of(3) == 9 # We may also want to override the second argument by using the following function calls. assert power_of(3, 2) == 9 diff --git a/src/functions/test_function_definition.py b/src/functions/test_function_definition.py index 82a0b50b..7fe8fc31 100644 --- a/src/functions/test_function_definition.py +++ b/src/functions/test_function_definition.py @@ -17,26 +17,30 @@ def fibonacci_function_example(number_limit): docstrings to automatically produce online or printed documentation, or to let the user interactively browse through code; it’s good practice to include docstrings in code that you write, so make a habit of it. + + + The execution of a function introduces a new symbol table used for the local variables of the + function. More precisely, all variable assignments in a function store the value in the local + symbol table; whereas variable references first look in the local symbol table, then in the + local symbol tables of enclosing functions, then in the global symbol table, and finally in + the table of built-in names. Thus, global variables cannot be directly assigned a value + within a function (unless named in a global statement), although they may be referenced. """ - # The execution of a function introduces a new symbol table used for the local variables of the - # function. More precisely, all variable assignments in a function store the value in the local - # symbol table; whereas variable references first look in the local symbol table, then in the - # local symbol tables of enclosing functions, then in the global symbol table, and finally in - # the table of built-in names. Thus, global variables cannot be directly assigned a value - # within a function (unless named in a global statement), although they may be referenced. fibonacci_list = [] previous_number, current_number = 0, 1 while previous_number < number_limit: - # The statement result.append(a) calls a method of the list object result. A method is a - # function that ‘belongs’ to an object and is named obj.methodname, where obj is some - # object (this may be an expression), and methodname is the name of a method that is - # defined by the object’s type. Different types define different methods. Methods of - # different types may have the same name without causing ambiguity. (It is possible to - # define your own object types and methods, using classes, see Classes) The method - # append() shown in the example is defined for list objects; it adds a new element at - # the end of the list. In this example it is equivalent to result = result + [a], but - # more efficient. + """ The statement result.append(a) calls a method of the list object result. A method is a + function that ‘belongs’ to an object and is named obj.methodname, where obj is some + object (this may be an expression), and methodname is the name of a method that is + defined by the object’s type. Different types define different methods. Methods of + different types may have the same name without causing ambiguity. (It is possible to + define your own object types and methods, using classes, see Classes) The method + append() shown in the example is defined for list objects; it adds a new element at + the end of the list. In this example it is equivalent to result = result + [a], but + more efficient. + """ + fibonacci_list.append(previous_number) # This is multiple assignment statement. We make current number to be previous one and the # sum of previous and current to be a new current. @@ -53,10 +57,11 @@ def test_function_definition(): # Now call the function we just defined. assert fibonacci_function_example(300) == [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233] - # A function definition introduces the function name in the current symbol table. The value of - # the function name has a type that is recognized by the interpreter as a user-defined function. - # This value can be assigned to another name which can then also be used as a function. This - # serves as a general renaming mechanism + """ A function definition introduces the function name in the current symbol table. The value of + the function name has a type that is recognized by the interpreter as a user-defined function. + This value can be assigned to another name which can then also be used as a function. This + serves as a general renaming mechanism + """ fibonacci_function_clone = fibonacci_function_example assert fibonacci_function_clone(300) == [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233] @@ -92,6 +97,7 @@ def call_func(func): other_name = 'John' return func(other_name) + assert call_func(greet_one_more) == 'Hello, John' # Functions can return other functions. In other words, functions generating other functions. @@ -105,12 +111,13 @@ def get_message(): greet_function = compose_greet_func() assert greet_function() == 'Hello there!' - # Inner functions have access to the enclosing scope. + """ Inner functions have access to the enclosing scope. - # More commonly known as a closure. A very powerful pattern that we will come across while - # building decorators. Another thing to note, Python only allows read access to the outer - # scope and not assignment. Notice how we modified the example above to read a "name" argument - # from the enclosing scope of the inner function and return the new function. + More commonly known as a closure. A very powerful pattern that we will come across while + building decorators. Another thing to note, Python only allows read access to the outer + scope and not assignment. Notice how we modified the example above to read a "name" argument + from the enclosing scope of the inner function and return the new function. + """ def compose_greet_func_with_closure(name): def get_message(): diff --git a/src/functions/test_function_documentation_string.py b/src/functions/test_function_documentation_string.py index b20d11a9..4686223f 100644 --- a/src/functions/test_function_documentation_string.py +++ b/src/functions/test_function_documentation_string.py @@ -24,17 +24,18 @@ def do_nothing(): def test_function_documentation_string(): - """Test documentation string.""" - - # The Python parser does not strip indentation from multi-line string literals in Python, so - # tools that process documentation have to strip indentation if desired. This is done using the - # following convention. The first non-blank line after the first line of the string determines - # the amount of indentation for the entire documentation string. (We can’t use the first line - # since it is generally adjacent to the string’s opening quotes so its indentation is not - # apparent in the string literal.) Whitespace “equivalent” to this indentation is then stripped - # from the start of all lines of the string. Lines that are indented less should not occur, but - # if they occur all their leading whitespace should be stripped. Equivalence of whitespace - # should be tested after expansion of tabs (to 8 spaces, normally). + """Test documentation string. + + The Python parser does not strip indentation from multi-line string literals in Python, so + tools that process documentation have to strip indentation if desired. This is done using the + following convention. The first non-blank line after the first line of the string determines + the amount of indentation for the entire documentation string. (We can’t use the first line + since it is generally adjacent to the string’s opening quotes so its indentation is not + apparent in the string literal.) Whitespace “equivalent” to this indentation is then stripped + from the start of all lines of the string. Lines that are indented less should not occur, but + if they occur all their leading whitespace should be stripped. Equivalence of whitespace + should be tested after expansion of tabs (to 8 spaces, normally). + """ assert do_nothing.__doc__ == """Do nothing, but document it. diff --git a/src/functions/test_function_keyword_arguments.py b/src/functions/test_function_keyword_arguments.py index 7b6ff1c3..134d0562 100644 --- a/src/functions/test_function_keyword_arguments.py +++ b/src/functions/test_function_keyword_arguments.py @@ -47,6 +47,8 @@ def test_function_keyword_arguments(): # 2 keyword arguments assert parrot(voltage=1000000, action='VOOOOOM') == message # 2 keyword arguments + + assert parrot(action='VOOOOOM', voltage=1000000) == message # 3 positional arguments diff --git a/src/modules/fibonacci_module.py b/src/modules/fibonacci_module.py index 2b50f175..7a2c6718 100644 --- a/src/modules/fibonacci_module.py +++ b/src/modules/fibonacci_module.py @@ -28,16 +28,17 @@ def fibonacci_smaller_than(limit): return result -# When you run a Python module with: -# -# >>> python fibonacci.py -# -# the code in the module will be executed, just as if you imported it, but with -# the __name__ set to "__main__". That means that by adding this code at the end of your module -# you can make the file usable as a script as well as an importable module, because the code that -# parses the command line only runs if the module is executed as the “main” file: -# -# >>> python fibonacci.py 50 +""" When you run a Python module with: + + #>>> python fibonacci.py + + the code in the module will be executed, just as if you imported it, but with + the __name__ set to "__main__". That means that by adding this code at the end of your module + you can make the file usable as a script as well as an importable module, because the code that + parses the command line only runs if the module is executed as the “main” file: + + #>>> python fibonacci.py 50 +""" if __name__ == '__main__': import sys print(fibonacci_smaller_than(int(sys.argv[1])))