Python函数式编程笔记。
Introducation
- Function Programming has a long history
 - List 1958
 - Renaissance: F#, Haskell, Erlang…
 - Used in industry
- Trading
 - Algorithmic
 - Telecommunication(Concurrency)
 
 
Features Of Functional Programming
- Everything is a function
 - Pure functions without side effects
 - Immutable data structures
 - Preserve state in functions
 - Recursion instead of loops / iteration
 
Advantages of Functional Programming
- Absence of side effects can make your programs more robust
 - Programs tend to be more modular come and typically in smaller building blocks
 - Better testable - call with same parameters always return same result
 - Focus on algorithms
 - Conceptional fit with parallel / concurrent programming
 - Live updates - Install new release while running
 
Disadvantages of Functional Programming
- Solutions to the same problem can look very different than procedural / object-oriented ones
 - Find good developers can be hard
 - Not equally useful for all types of problems
 - Input/output are side effects and need special treatment
 - Recursion is “an order of magnitude more complex” than loops/iteration
 - Immutable data structures may increase run times
 
Python’s Functional Features - Overview
- Pure functions (sort of)
 - Closures - hold state in functions
 - Functions as object and decorators
 - Immutable data types
 - Lazy evaluation - generators
 - List(dictionary, set) comprehensions
 - functions, itertools, lambda, map, filter
 - Recursion - try to avoid, recursion limit has a reason
 
Pure Functions - No Side Effects
- No side effect, return value only
 - “Shallow copy” problem
 
1  | def dp_pure(data):  | 
- An ooverloaded * that modifies data or causes other side effects would make the function un-pure
 - No guarantee of pureness
 - Pure functions by convention
 
Side effects
- Side effects are common
 
1  | def do_side_effect(my_list):  | 
Functions are Objects
1  | def func1():  | 
1  | >>>> my_funcs = {'a': func1, 'b': func2}  | 
- Everything is an object
 
Closures and “Currying”
1  | def outer(outer_arg):  | 
Partail Functions
- Module functools offers some tools for the Functional approach
 
1  | import functools  | 
Recursion
1  | def loop(n):  | 
Recursion - Time it in IPython
1  | %timeit loop(le3)  | 
- sys.setrecursionalimit(int(le6)) and %timeit recurse(le5) segfaulted my IPython kernel
 
Lambda
- Allow versy limited anonymous functions
 - Expressions only, no statements
 - Past discussion to exclude it from Python 3
 - Useful for callbacks
 
1  | def use_callback(callback, arg):  | 
Lambda - Not Essential
- Always possible to add two extra lines
 - Write a function with name and docstring
 
1  | def double(arg):  | 
List Comprehensions instead of map
- Typical use of map
 
1  | >>> map(lambda arg: arg * 2, range(2, 6))  | 
- Replace with list comprehension
 
1  | [x * 2 for x in range(2, 6)]  | 
List Comprehensions instead of filter
- Typical use of filter
 
1  | >>> filter(lambda x: x > 10, range(5, 16))  | 
- Replace with list comprehension
 
1  | >> [x for x in range(5, 16) if x > 10]  | 
Decorators
- Application of closures
 
1  | import functools  | 
Immutable Data Types - Tuples Instead of Lists
1  | my_list = range(10)  | 
- Contradicts the usage recommendation
- Lists == elements of the same kind
 - Tuple == “named” elements
 
 
Immutable Data Types - Freeze Sets
1  | my_set = set(range(5))  | 
- Can be used as dictionary keys
 
Not Only Functional
- Pure functional programs can be difficult to implement
 - Combine with procedural and object-oriented program parts
 - Choose right tool, for the task at hand
 - Develop a feeling where a functional approach can be beneficial
 
Avoid Side effects
1  | class MyClass(object):  | 
- Set all attributes in init (Pylint will remind you)
 - Actual useful application of static methods
 - Fewer side effects than setting attributes outside init
 - Your beloved classes and instances are still here
 - Inheritance without overriding init and use super,child class implements own make_attr1()
 
Freeze Classes
1  | class Reader(object):  | 
- Mutable data structures are useful for reading data
 - “Freeze” to get read-only version
 - No future, unwanted modifications possible
 
Freeze Classes - One Liner Version
- Still kind of readable
 
1  | class Reader(object):  | 
Stepwise Freezing and Thawing I
1  | class FrozenUnFrozen(object):  | 
Stepwise Freezing and Thawing II
1  | >>> fuf = FrozenUnFrozen()  | 
Use Case for Freezing
- Legacy code: Where are data modified?
 - Complex systems: Detect unwanted modifications
 
Immutable Data Structures - Counter Arguments
- Some algorithms maybe diffcult to implement
 - Can be rather inefficient - repeated re-allocation of memory
- Antipattern string concatanation
 
 
1  | >> s += 'text'  | 
- Try this in Jypthon and (standrad-)PyPy
 
Lazy Evaluation
- Iterators and generators
 
1  | >> [ x * 2 for x in xrange(5)]  | 
- Saves memory and possibly CPU time
 
Itertools - “Lazy Programmers are Good Programmers”
- Module itertools offers tools for the work with iteratoes
 
1  | it.izip('abc', 'xyz')  | 
1  | list(it.islice(iter(range(10)), None, 8, 2))  | 
Pipelining -Chaining Commands
- Generators make good pipelines
 - Useful for workflow problems
 - Example parsing of a log file
 
Generators - Pull
- Log file:
 
1  | 35  | 
Generators - Pull - Import
1  | import sys  | 
Generators - Pull - Read File
1  | def read_forever(fobj):  | 
Generators - Pull - Filter Out Comment lines
1  | def filter_comments(lines):  | 
Generators - Pull - Convert Numbers
1  | def get_number(lines):  | 
Generators - Pull - Initialize the Process I
1  | def show_sum(file_name = 'oyr.txt'):  | 
Coroutines - Push
- Log file:
1
2
3
4
5
6
7Error: 78
DEBUG: 72
WAN: 99
CRITICAL: 97
Error: 78
Error: 89
Error: 46 
Coroutines - Push -Initialize with a Decorator
1  | def init_coroutine(func):  | 
Coroutines - Push - Read the File
def read_forever(fobj, target):
  counter = 0
  while True:
    line = fobj.readline()
    if not line:
      time.sleep(0.1)
      continue
    target.send(line)
Coroutines - Push - Filter Out Comments
1  | 
  | 
Coroutines - Push - Convert Numbers
1  | @init_coroutine  | 
Coroutines - Push - Consumer I
1  | 
  | 
Coroutines - Push - Consumer II
1  | 
  | 
Coroutines - Push - All Consumers
1  | TARGETS = {  | 
Conroutines - Push - Initialize
1  | def show_sum(file_name='out.txt'):  | 
def show_sum(file_name=’out.txt’):
  read_forever(open(file_name), filter_comments(get_number(TARGETS)))
if name == ‘main‘:
  show_sum(sys.argv[1])
Conclusions
- Python offers useful functional features
 - But it is no pure functional language
 - For some tasks the functional approach works veru well
 - For some others much less
 - Combine and switch back and forth with oo and procedural style
& “Stay pythonic, be pragmatic”