Effective Python(2): Functions

Item 14: Prefer Exceptions to Returning None

  • Functions that return None to indicate special meaning are error prone because None and other values (e.g., zero, the empty string) all evaluate to False in conditional expressions.
  • Raise exceptions to indicate special situations instead of return None. Expect the calling code to handle exceptions properly when they are documented.

Item 15: Know How Closures Interact with Variable Scope

When you reference a variable in an expression, the Python interpreter will traverse the scope to resolve the reference in this order:

  • The current function's scope
  • Any enclosing scopes (like other containing functions)
  • The scope of the module that contains the code (also called the global scope)
  • The build-in scope (that contains functions like len and str)

Assigning a value to a variable works differently:

  • If the variable is already defined in the current scope, then it will just take on the new value.
  • If the variable doesn't exist in the current scope, then Python treats the assignment as a variable definition.

Summary

  • In Python 3, use the nonlocal statement to indicate when a closure can modify a variable in its enclosing scopes. But it is not recommended for it may cause confusion. Avoid using nonlocal statements for anything beyond simple functions.
  • In Python 2, use a mutable value (like a single-item list) to work around the lack of the nonlocal statement. Once mutable value is retrieved, the closure can modify the state of the value to send data out of the inner scope.

Item 16: Consider Generators Instead of Returning Lists

  • The iterator returned by a generator produces the set of values passed to yield expressions within the generator function's body;
  • Generators can produce a sequence of outputs for arbitrarily large inputs because their working memory doesn't include all inputs and outputs.

Item 17: Be Defensive When Iterating Over Arguments

  • Beware of functions that iterate over input arguments multiple times. If these arguments are iterators, you may see strange behavior and missing values;
  • Python’s iterator protocol defines how containers and iterators interact with the iter and next built-in functions, for loops, and related expressions;
  • You can easily define your own iterable container type by implementing the __iter__ method as a generator.
  • You can detect that a value is an iterator (instead of a container) if calling iter on it twice produces the same result, which can then be progressed with the next built-in function.

Item 18: Reduce Visual Noise with Variable Positional Arguments

  • Functions can accept a variable number of positional arguments by using *args in the def statement;
  • You can use the items from a sequence as the positional arguments for a function with the * operator;
  • Using the * operator with a generator may cause your program to run out of memory and crash. The variable arguments are always turned into a tuple before they are passed to your function. This means that if the caller of your function uses the * operator on a generator, it will be iterated until it’s exhausted.
  • Adding new positional parameters to functions that accept *args can introduce hard-to-find bugs.

Item 19: Provide Optional Behavior with Keyword Arguments

  • Function arguments can be specified by position or by keyword;
  • Keyword arguments with default values make it easy to add new behaviors to a function, especially when the function has existing callers;
  • Optional keyword arguments should always be passed by keyword instead of by position.

Item 20: Use None and Docstrings to Specify Dynamic Default Arguments

  • Default arguments are only evaluated once: during function definition at module load time. This can cause odd behaviors for dynamic values (like {} or []).
  • Use None as the default value for keyword arguments that have a dynamic value. Document the actual default behavior in the function’s docstring.

Item 21: Enforce Clarity with Keyword-Only Arguments

  • Use keyword-only arguments to force callers to supply keyword arguments for potentially confusing functions, especially those that accept multiple Boolean flags.
  • Python 3 supports explicit syntax for keyword-only arguments in functions. The * symbol in the argument list indicates the end of positional arguments and the beginning of keyword-only arguments.
  • Python 2 can emulate keyword-only arguments for functions by using **kwargs and manually raising TypeError exceptions.
posted @ 2016-01-29 14:27  luckysimple  阅读(194)  评论(0编辑  收藏  举报