From 947bdef1c2b1681ea0a4db41c3f507309f2d8eae Mon Sep 17 00:00:00 2001 From: Jay Roberts Date: Fri, 11 May 2012 14:08:08 -0400 Subject: [PATCH 1/7] Fix minor spelling, grammar, and consistency errors in the Structure is Key section. --- docs/writing/structure.rst | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/writing/structure.rst b/docs/writing/structure.rst index 38fde94e9..7d35b9669 100644 --- a/docs/writing/structure.rst +++ b/docs/writing/structure.rst @@ -8,49 +8,49 @@ Structuring your project properly is extremely important. Structure is Key ---------------- -Thanks to the way imports and module are handled in Python, it is +Thanks to the way imports and modules are handled in Python, it is relatively easy to structure a python project. Easy, here, means -actually that you have not many constraints and that the module -importing model is easy grasp. Therefore, you are left with the -pure architectural task of drawing the different parts of your +that you do not have many constraints and that the module +importing model is easy to grasp. Therefore, you are left with the +pure architectural task of crafting the different parts of your project and their interactions. -Easy structuration of a project means it is also easy -to do it poorly. Some signs of a poorly structured projects +Easy structuring of a project means it is also easy +to do it poorly. Some signs of a poorly structured project include: -- Multiple and messy circular dependencies: if your classes +- Multiple and messy circular dependencies: If your classes Table and Chair in furn.py need to import Carpenter from workers.py - to answer to a question such as table.isdoneby(), - and if convertly the class Carpenter need to import Table and Chair, - for example to answer to carpenter.whatdo(), then you - have a circular dependency, and will have to resort to + to answer a question such as table.isdoneby(), + and if conversely the class Carpenter needs to import Table and Chair, + to answer the question carpenter.whatdo(), then you + have a circular dependency. In this case you will have to resort to fragile hacks such has using import statements inside methods or functions. -- Hidden coupling. Each and every change in Table implementation +- Hidden coupling: Each and every change in Table's implementation breaks 20 tests in unrelated test cases because it breaks Carpenter's code, which requires very careful surgery to adapt the change. This means you have too many assumptions about Table in Carpenter's code or the reverse. -- Heavy usage of global state or context: Instead of explicitely +- Heavy usage of global state or context: Instead of explicitly passing ``(height, width, type, wood)`` to each other, Table and Carpenter rely on global variables that can be modified - and are modified on the fly by different agent. You need to - scrutinize all access to this global variables to understand why - a rectangular table became a sqaure, and discover that a remote + and are modified on the fly by different agents. You need to + scrutinize all access to these global variables to understand why + a rectangular table became a square, and discover that remote template code is also modifying this context, messing with table dimensions. - Spaghetti code: Multiple pages of nested if clauses and for loops with a lot of copy-pasted procedural code and no proper segmentation are known as spaghetti code. Python's - meaningful indentation (one of its most controversial feature) make + meaningful indentation (one of its most controversial features) make it very hard to maintain this kind of code. So the good news is that you might not see too much of it. -- Ravioli code is more likely in Python: it consists of hundreds of +- Ravioli code is more likely in Python: It consists of hundreds of similar little pieces of logic, often classes or objects, without proper structure. If you never can remember if you have to use FurnitureTable, AssetTable or Table, or even TableNew for your From 13865eecae0c9763fa275ecdb7be937ad00fd8b2 Mon Sep 17 00:00:00 2001 From: Jay Roberts Date: Fri, 11 May 2012 14:20:14 -0400 Subject: [PATCH 2/7] Fix minor spelling, grammar, and consistency errors in the Modules section. --- docs/writing/structure.rst | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/writing/structure.rst b/docs/writing/structure.rst index 7d35b9669..e30aee063 100644 --- a/docs/writing/structure.rst +++ b/docs/writing/structure.rst @@ -60,53 +60,53 @@ include: Modules ------- -Python modules are one of the main abstraction layer available and probably the +Python modules are one of the main abstraction layers available and probably the most natural one. Abstraction layers allow separating code into parts holding -related data and functionalities. +related data and functionality. For example, a layer of a project can handle interfacing with user actions, while another would handle low-level manipulation of data. The most natural way -to separate these two layers is to regroup all interfacing functionalities +to separate these two layers is to regroup all interfacing functionality in one file, and all low-level operations in another file. In this case, -the interface file need to import the low-level file. This is done with the +the interface file needs to import the low-level file. This is done with the `import` and `from ... import` statements. -As soon as you use `import` statements you use modules, either builtin modules -such as `os` and `sys`, or third-party modules you have installed in your -environment, or project's internal modules. +As soon as you use `import` statements you use modules. These can be either built-in +modules such as `os` and `sys`, third-party modules you have installed in your +environment, or your project's internal modules. Nothing special is required for a Python file to be a module, but the import -mechanism need to be understood in order to use this concept properly and avoid +mechanism needs to be understood in order to use this concept properly and avoid some issues. Concretely, the `import modu` statement will look for the proper file, which is `modu.py` in the same directory as the caller if it exists. If it is not -found, the Python interpreter with search for `modu.py` in the "path" +found, the Python interpreter will search for `modu.py` in the "path" recursively and raise an ImportError exception if it is not found. Once `modu.py` is found, the Python interpreter will execute the module in an isolated scope. Any top-level statement in `modu.py` will be executed, -including other imports if any. Function and classes definitions are stored in +including other imports if any. Function and class definitions are stored in the module's dictionary. -Then modules variables, functions and classes will be available to the caller +Then, the module's variables, functions, and classes will be available to the caller through the module's namespace, a central concept in programming that is particularly helpful and powerful in Python. -In many languages, a `include file` directive is used by the preprocessor to -take all code found in the file and 'copy' it in the caller's code. It is +In many languages, an `include file` directive is used by the preprocessor to +take all code found in the file and 'copy' it into the caller's code. It is different in Python: the included code is isolated in a module namespace, which means that you generally don't have to worry that the included code could have -unwanted effect, eg override an existing function with the same name. +unwanted effects, e.g. override an existing function with the same name. It is possible to simulate the more standard behavior by using a special syntax of the import statement: `from modu import *`. This is generally considered bad -practice, **using import * makes code harder to read and dependencies less -compartimented**. +practice. **Using `import *` makes code harder to read and makes dependencies less +compartmentalized**. Using `from modu import func` is a way to pinpoint the function you want to -import and put it is the global namespace. While much less harmful than `import -*` because it shows explicitely what is imported in the global namespace, it's +import and put it in the global namespace. While much less harmful than `import +*` because it shows explicitly what is imported in the global namespace, its advantage over a simpler `import modu` is only that it will save some typing. **Very bad** @@ -134,13 +134,13 @@ advantage over a simpler `import modu` is only that it will save some typing. [...] x = modu.sqrt(4) # sqrt is visibly part of modu's namespace -As said in the section about style, readability is one of the main feature of +As said in the section about style, readability is one of the main features of Python. Readability means to avoid useless boilerplate text and clutter, therefore some efforts are spent trying to achieve a certain level of brevity. -But terseness and obscurity are the limits where brevity should stop: being -able to tell immediately from where comes a class or a function, as in the -`modu.func` idiom, improves greatly code readability and understandability in -most cases but the simplest single file projects. +But terseness and obscurity are the limits where brevity should stop. Being +able to tell immediately where a class or function comes from, as in the +`modu.func` idiom, greatly improves code readability and understandability in +all but the simplest single file projects. Packages From e79aa476a0921240e6fa4a693370ab28e51981e8 Mon Sep 17 00:00:00 2001 From: Jay Roberts Date: Fri, 11 May 2012 14:24:49 -0400 Subject: [PATCH 3/7] Fix minor spelling, grammar, and consistency errors in the Packages section. --- docs/writing/structure.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/writing/structure.rst b/docs/writing/structure.rst index e30aee063..bb7ebea03 100644 --- a/docs/writing/structure.rst +++ b/docs/writing/structure.rst @@ -149,29 +149,29 @@ Packages Python provides a very straightforward packaging system, which is simply an extension of the module mechanism to a directory. -Any directory with a __init__.py file is considered a Python package. The +Any directory with an __init__.py file is considered a Python package. The different modules in the package are imported in a similar manner as plain -modules, will a special behavior for the __init__.py file, that is used to +modules, but with a special behavior for the __init__.py file, which is used to gather all package-wide definitions. A file modu.py in the directory pack/ is imported with the statement `import -pack.modu`. This statement will look for a __init__.py file in `pack`, execute -all its top-level statements. Then it will look for a file `pack/modu.py` and -execute all its top-level statements. After these operations, any variable, -function or class defined in modu.py is available in pack.modu namespace. +pack.modu`. This statement will look for an __init__.py file in `pack`, execute +all of its top-level statements. Then it will look for a file `pack/modu.py` and +execute all of its top-level statements. After these operations, any variable, +function, or class defined in modu.py is available in the pack.modu namespace. -A commonly seen issue is to add too many code and functions in __init__.py +A commonly seen issue is to add too much code to __init__.py files. When the project complexity grows, there may be sub-packages and -sub-sub-packages in a deep directory structure, and then, import a single item -from a sub-sub-package will require to execute all __init__.py file met while -descending the tree. +sub-sub-packages in a deep directory structure, and then, importing a single item +from a sub-sub-package will require executing all __init__.py files met while +traversing the tree. -Leaving a __init__.py file empty is considered normal and even a good pratice, +Leaving an __init__.py file empty is considered normal and even a good practice, if the package's modules and sub-packages do not need to share any code. Lastly, a convenient syntax is available for importing deeply nested packages: -`import very.deep.module as mod` allow to use `mod` in place of the verbose -repetition of `very.deep.module` in front of each calls to module items. +`import very.deep.module as mod`. This allows you to use `mod` in place of the verbose +repetition of `very.deep.module`. Object-oriented programming --------------------------- From cca9272a7f85c19fec02c9cbce78b4c7d25f1713 Mon Sep 17 00:00:00 2001 From: Jay Roberts Date: Fri, 11 May 2012 14:37:56 -0400 Subject: [PATCH 4/7] Fix minor spelling, grammar, and consistency errors in the Object-oriented programming section. The paragraph about context and side effects was very difficult to parse and contained the word "decelable". I attempted to capture the meaning of the paragraph in a way that was more readable. --- docs/writing/structure.rst | 44 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/writing/structure.rst b/docs/writing/structure.rst index bb7ebea03..a4e4c1ebe 100644 --- a/docs/writing/structure.rst +++ b/docs/writing/structure.rst @@ -176,8 +176,8 @@ repetition of `very.deep.module`. Object-oriented programming --------------------------- -Python is sometime described as an object-oriented programming language. This -can be somewhat misleading and need to be clarified. +Python is sometimes described as an object-oriented programming language. This +can be somewhat misleading and needs to be clarified. In Python, everything is an object, and can be handled as such. This is what is meant when we say that, for example, functions are first-class objects. @@ -188,46 +188,46 @@ object-oriented language. However, unlike Java, Python do not impose object-oriented programming as the main programming paradigm. It is perfectly viable for a Python project to not -be object-oriented, ie. to use no or very few class definitions, class -inheritance, and any other mechanism that are specific to object-oriented +be object-oriented, i.e. to use no or very few class definitions, class +inheritance, or any other mechanisms that are specific to object-oriented programming. Moreover, as seen in the modules_ section, the way Python handles modules and -namespaces gives directly to the developer a natural way to ensure +namespaces gives the developer a natural way to ensure encapsulation and separation of abstraction layers, both being the most common reasons to use object-orientation. Therefore, Python programmers have more latitude to not use object-orientation, when it is not required by the business -model to be constructed. +model. -There are some reasons to avoid unnecessary object-orientation. Definining +There are some reasons to avoid unnecessary object-orientation. Defining custom classes is useful when we want to glue together some state and some -functionality. The problem, as pointed out by the discussions about functional +functionality. The problem, as pointed out by the discussions about functional programming, comes from the "state" part of the equation. -In some architectures, typically web applications, instances of Python -processes are spawned simultaneously to answer to external requests that can -happen at the same time. In this case, holding some state into instanciated +In some architectures, typically web applications, multiple instances of Python +processes are spawned to respond to external requests that can +happen at the same time. In this case, holding some state into instantiated objects, which means keeping some static information about the world, is prone -to concurrency problems or race-conditions: between the initialization of the +to concurrency problems or race-conditions. Sometime between the initialization of the state of an object, usually done with the __init__() method, and the actual use -of the object state through one of its method, the world may have changed, and +of the object state through one of its methods, the world may have changed, and the retained state may be outdated. For example, a request may load an item in memory and mark it as read by a user. If another request requires the deletion -of this item at the same, it may happen that the deletion actually occur after +of this item at the same, it may happen that the deletion actually occurs after the first process loaded the item, and then we have to mark as read a deleted object. This and other issues led to the idea that using stateless functions is a better programming paradigm. -Another way to say the same thing is to propose to use functions and procedures -with as few implicit context and side-effects as possible. A function's -implicit context is decelable when the function body refers to some global -variables or fetches data from the persistence layer. Side-effects are the -opposite: if a function body modifies the global context or save or delete data -on the persistence layer, it is said to have side-effect. +Another way to say the same thing is to suggest using functions and procedures +with as few implicit contexts and side-effects as possible. A function's +implicit context is made up of any of the global variables or items in the persistence layer +that are accessed from within the function. Side-effects are the changes that a function makes +to it's implicit context. If a function saves or deletes data in a global variable or +in the persistence layer, it is said to have a side-effect. -Isolating carefully functions with context and side-effects from functions with +Carefully isolating functions with context and side-effects from functions with logic (called pure functions) allow the following benefits: - Pure functions are more likely to be deterministic: given a fixed input, @@ -239,7 +239,7 @@ logic (called pure functions) allow the following benefits: - Pure functions are easier to test with unit-tests: There is less need for complex context setup and data cleaning afterwards. -- Pure functions are easier to manipulate, decorate_, pass-around. +- Pure functions are easier to manipulate, decorate_, and pass-around. In summary, pure functions, without any context or side-effects, are more efficient building blocks than classes and objects for some architectures. From 2dd87bea16572d143b31c5ac33656740a2dc2c88 Mon Sep 17 00:00:00 2001 From: Jay Roberts Date: Fri, 11 May 2012 14:39:58 -0400 Subject: [PATCH 5/7] Fix minor spelling, grammar, and consistency errors in the Decorators section. --- docs/writing/structure.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/writing/structure.rst b/docs/writing/structure.rst index a4e4c1ebe..5f4bdfe2c 100644 --- a/docs/writing/structure.rst +++ b/docs/writing/structure.rst @@ -253,12 +253,12 @@ relatively long life of their own in the computer's memory. Decorators ---------- -Python language provides a simple yet powerful syntax called 'decorators'. +The Python language provides a simple yet powerful syntax called 'decorators'. A decorator is a function or a class that wraps (or decorate) a function or a method. The 'decorated' function or method will replace the original -'undecorated' function or method. Because function are first-class objects +'undecorated' function or method. Because functions are first-class objects in Python it can be done 'manually' but using the @decorator syntax is -clearer and thus prefered. +clearer and thus preferred. .. code-block:: Python @@ -277,8 +277,8 @@ clearer and thus prefered. # bar() is decorated Using this mechanism is useful for separating concerns and avoiding -external un-related logic to 'pollute' the core logic of the function -or method. A good example of a functionality that is better handled +external un-related logic 'polluting' the core logic of the function +or method. A good example of a piece of functionality that is better handled with decoration is memoization or caching: you want to store the results of an expensive function in a table and use them directly instead of recomputing them when they have already been computed. This is clearly not part From a27f9d25a8931353c42c39ededf1ee98d97aeee1 Mon Sep 17 00:00:00 2001 From: Jay Roberts Date: Fri, 11 May 2012 14:46:07 -0400 Subject: [PATCH 6/7] Fix minor spelling, grammar, and consistency errors in the Dynamic Typing section. --- docs/writing/structure.rst | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/writing/structure.rst b/docs/writing/structure.rst index 5f4bdfe2c..040df0abc 100644 --- a/docs/writing/structure.rst +++ b/docs/writing/structure.rst @@ -290,18 +290,18 @@ Dynamic typing Python is said to be dynamically typed, which means that variables do not have a fixed type. In fact, in Python, variables are very different from what they are in many other languages, specifically -strongly-typed languages: variables are not a segment of the computer's -memory where some value ir written, they are 'tags' or 'names' pointing +strongly-typed languages. Variables are not a segment of the computer's +memory where some value is written, they are 'tags' or 'names' pointing to objects. It is therefore possible for the variable 'a' to be set to the value 1, then to the value 'a string', then to a function. -The dynanic typing of Python is often considered as a weakness, and indeed -it can lead to complexities and to hard-to-debug code, where something +The dynamic typing of Python is often considered to be a weakness, and indeed +it can lead to complexities and hard-to-debug code. Something named 'a' can be set to many different things, and the developer or the -maintainer need to track this name in the code to make sure it has not +maintainer needs to track this name in the code to make sure it has not been set to a completely unrelated object. -Some guidelines allow to avoid this issue: +Some guidelines help to avoid this issue: - Avoid using variables for different things. @@ -323,9 +323,8 @@ Some guidelines allow to avoid this issue: def func() pass # Do something -Using short functions or methods helps writing good code for many -reasons, one being that their local scope is clearer, and the risk -of using the same name for two unrelated things is lowered. +Using short functions or methods helps reduce the risk +of using the same name for two unrelated things. It is better to use different names even for things that are related, when they have a different type: @@ -340,14 +339,14 @@ when they have a different type: There is no efficiency gain when reusing names: the assignments will have to create new objects anyway. However, when the complexity -grows are each assignment are separated by other lines of code, including -'if' branches and loops, it becomes harder to acertain which type is the -variable at hand. - -Some coding practices, like functional programming, even recommend to never re-assign a variable, which -is done in Java with the keyword final. Python do not have such a keyword, -and it would be against its philosophy anyway, but it may be a good -discipline to avoid setting more than once any variable, and it helps +grows and each assignment is separated by other lines of code, including +'if' branches and loops, it becomes harder to ascertain what a given +variable's type is. + +Some coding practices, like functional programming, recommend never reassigning a variable. +In Java this is done with the `final` keyword. Python does not have a `final` keyword +and it would be against its philosophy anyway. However, it may be a good +discipline to avoid assigning to a variable more than once, and it helps in grasping the concept of mutable and immutable types. Mutable and immutable types From 90b12de0bdbf33bbdefd5d2e15b7a8b04a9f6fad Mon Sep 17 00:00:00 2001 From: Jay Roberts Date: Fri, 11 May 2012 14:48:32 -0400 Subject: [PATCH 7/7] Fix minor spelling, grammar, and consistency errors in the Mutable and immutable types. --- docs/writing/structure.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/writing/structure.rst b/docs/writing/structure.rst index 040df0abc..e37ba81c7 100644 --- a/docs/writing/structure.rst +++ b/docs/writing/structure.rst @@ -356,12 +356,12 @@ Python has two kinds of built-in or user-defined types. Mutable types are those that allow in-place modification of the content. Typical mutables are lists and dictionaries: -All lists have muting methods, like append() or pop(), and -can be modified in place. Same for dictionaries. +All lists have mutating methods, like append() or pop(), and +can be modified in place. The same goes for dictionaries. Immutable types provide no method for changing their content. For instance, the variable x set to the integer 6 has no "increment" method. If you -want to computed x + 1, you have to create another integer and give it +want to compute x + 1, you have to create another integer and give it a name. .. code-block:: python @@ -385,8 +385,8 @@ For example, the immutable equivalent of a list is the tuple, created with ``(1, 2)``. This tuple is a pair that cannot be changed in-place, and can be used as a key for a dictionary. -One particularity of Python that can surprise in the beginning is that -string are immutable. This means that when constructing a string from +One peculiarity of Python that can surprise beginners is that +strings are immutable. This means that when constructing a string from its parts, it is much more efficient to accumulate the parts in a list, which is mutable, and then glue ('join') the parts together when the full string is needed.