mirror of
https://github.com/kennethreitz/python-guide.git
synced 2026-06-05 14:50:19 +00:00
Merge pull request #134 from guibog/master
Adding decorators, typing and (im)mutability
This commit is contained in:
@@ -249,6 +249,148 @@ things that are manipulated (windows, buttons, avatars, vehicles) have a
|
||||
relatively long life of their own in the computer's memory.
|
||||
|
||||
|
||||
Decorators
|
||||
----------
|
||||
|
||||
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
|
||||
in Python it can be done 'manually' but using the @decorator syntax is
|
||||
clearer and thus prefered.
|
||||
|
||||
.. code-block:: Python
|
||||
|
||||
def foo():
|
||||
# do something
|
||||
|
||||
def decorator(func):
|
||||
# manipulate func
|
||||
return func
|
||||
|
||||
foo = decorator(foo) # Manually decorate
|
||||
|
||||
@decorator
|
||||
def bar():
|
||||
# Do something
|
||||
# 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
|
||||
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
|
||||
of the function logic.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
been set to a completely unrelated object.
|
||||
|
||||
Some guidelines allow to avoid this issue:
|
||||
|
||||
- Avoid using variables for different things.
|
||||
|
||||
**Bad**
|
||||
|
||||
.. code-block:: Python
|
||||
|
||||
a = 1
|
||||
a = 'a string'
|
||||
def a():
|
||||
pass # Do something
|
||||
|
||||
**Good**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
count = 1
|
||||
msg = 'a string'
|
||||
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.
|
||||
|
||||
It is better to use different names even for things that are related,
|
||||
when they have a different type:
|
||||
|
||||
**Bad**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
items = 'a b c d' # This is a string...
|
||||
items = items.split(' ') # ...becoming a list
|
||||
items = set(items) # ...and then a set
|
||||
|
||||
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
|
||||
in grasping the concept of mutable and immutable types.
|
||||
|
||||
Mutable and immutable types
|
||||
---------------------------
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
a name.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
my_list = [1, 2, 3]
|
||||
my_list[0] = 4
|
||||
print my_list # [4, 2, 3] <- The same list as changed
|
||||
|
||||
x = 6
|
||||
x = x + 1 # The new x is another object
|
||||
|
||||
One consequence of this difference in behavior is that mutable
|
||||
types are not "stable", and therefore cannot be used as dictionary
|
||||
keys.
|
||||
|
||||
Using properly mutable types for things that are mutable in nature
|
||||
and immutable types for things that are fixed in nature
|
||||
helps to clarify the intent of the code.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
Vendorizing Dependencies
|
||||
------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user