This article mainly introduces some more in-depth features of Python function programming. This article is from the IBM official Developer Technical Documentation. if you need it, please refer to it.
Bind
Careful readers may remember the limitations I have pointed out in Part 1 of function technology. In Python, rebinding of the name of a function expression cannot be avoided. In FP, names are generally abbreviated as long expressions, but this statement implies that "the same expression always finds the same value ". This suggestion is not true if the marked name is rebound. For example, let's define some shortcut expressions used in function programming, such:
Listing 1. rebinding of the following Python FP section causes a fault
>>> car = lambda lst: lst[0]>>> cdr = lambda lst: lst[1:]>>> sum2 = lambda lst: car(lst)+car(cdr(lst))>>> sum2(range(10))1>>> car = lambda lst: lst[2]>>> sum2(range(10))5
Unfortunately, the identical expression sum2 (range (10) returns two different values at two places in the program, even if the expression itself does not use any variable in its parameters.
Fortunately, the functional module provides a class called Bindings (proposed to the Keller) to prevent such rebinding (at least occasionally, python will not block programmers who want to unbind the SDK ). However, using Bindings requires some additional syntax, which is not easy to happen. In the example of the functional module, Keller names the Bindings instance as let (I assume it is behind the let keyword of the ML family language ). For example, we will do this:
Listing 2. Python FP with secure rebinding
>>> from functional import *>>> let = Bindings()>>> let.car = lambda lst: lst[0]>>> let.car = lambda lst: lst[2]Traceback (innermost last): File "
", line 1, in ? File "d:\tools\functional.py", line 976, in __setattr__ raise BindingError, "Binding '%s' cannot be modified." % namefunctional.BindingError: Binding 'car' cannot be modified.>>> car(range(10))0
Obviously, the real program must make some settings to capture "binding errors", and they are thrown to avoid the emergence of a type of problem.
Together with Bindings, functional provides the namespace function to obtain the namespace (actually a dictionary) from the Bindings instance ). This is easy to implement if you want to calculate an expression in the (immutable) namespace defined in Bindings. Python's eval () function allows computation in the namespace. Let's use an example to figure out:
Listing 3. using the Python FP section of an immutable namespace
>>> let = Bindings() # "Real world" function names>>> let.r10 = range(10)>>> let.car = lambda lst: lst[0]>>> let.cdr = lambda lst: lst[1:]>>> eval('car(r10)+car(cdr(r10))', namespace(let))>>> inv = Bindings() # "Inverted list" function names>>> inv.r10 = let.r10>>> inv.car = lambda lst: lst[-1]>>> inv.cdr = lambda lst: lst[:-1]>>> eval('car(r10)+car(cdr(r10))', namespace(inv))17
Closure
FP has an interesting concept-closure. In fact, closures are very interesting for many developers, even in non-functional languages such as Perl and Ruby. In addition, Python 2.1 is currently trying to add the word range restriction function, which will provide most of the closure functions.
What is a closure? Steve Majeski recently provided a good description of this concept in the Python Newsgroup:
The object is the data of the accompanying process ...... Closure is the process of attaching data.
The closure is like the FP Jekyll's Hyde for OOP (role or may also be called ). The closure is similar to an object example, which is a way to encapsulate a large amount of data and functions.
Let's go back to the previous section to learn about the problems solved by objects and closures, and how to solve the problems if none of them are solved. The result returned by a function is usually determined by the context used in its calculation. The most common -- or possibly the most obvious -- the method for specifying the context is to pass some parameters to the function to notify the function of the value to process. But sometimes there is a fundamental difference between the "background" and "foreground" parameters-the difference between what the function is processing at this specific time and the "configuration" of the function for multiple potential calls.
There are many background approaches to focus on the foreground. One of them is a simple method of "biting out a bullet", which transmits every parameter required by the function during each call. This method is usually used in the call chain. as long as a value may be required in some places, some values (or structures with multiple members) will be passed ). The following is a small example:
Listing 4. displaying the Python section of the cargo variable
>>> defa (n):... add7 = b(n)... return add7...>>> defb (n):... i = 7... j = c(i,n)... return j...>>> defc (i,n):... return i+n...>>> a(10) # Pass cargo value for use downstream17
In the cargo example, in B (), n has no effect in addition to being passed to c. Another way is to use global variables:
Listing 5. Python section showing global variables
>>> N = 10 >>> defaddN (I ):... global N... return I + N... >>> addN (7) # Add global N to argument17 >>> N = 20 >>> addN (6) # Add global N to argument26 global variable N in any desire to call addN () but there is no need to clearly transmit the global context "context ". Another more Python-specific technique is to "freeze" a variable when it is defined into a function that uses default parameters: Listing 6. show the Python section for freezing variables >>> N = 10 >>> defaddN (I, n = N ):... return I + n... >>> addN (5) # Add 1015 >>> N = 20 >>> addN (6) # Add 10 (current N doesn't matter) 16
A frozen variable is essentially a closure. Some data is "affiliated" to the addN () function. For the complete closure, when addN () is defined, all data will be available during the call. However, in this example (or many more robust examples), the use of default parameters is simple enough. Variables that have never been used by addN () do not affect the calculation.
Next let's look at an OOP method that is closer to the real problem. The time of the year reminds me of the tax programs that collect various types of data in the "meeting" style-without having to have a specific order-and eventually use all the data for calculation. Let's create a simple version:
Listing 7. Python-style tax calculation class/example
class TaxCalc: deftaxdue (self): return (self.income-self.deduct)*self.ratetaxclass = TaxCalc()taxclass.income = 50000taxclass.rate = 0.30taxclass.deduct = 10000 print "Pythonic OOP taxes due =", taxclass.taxdue()
In the TaxCalc class (or its instance), some data can be collected-in any order-once all the elements are obtained, you can call the method of this object to complete the calculation of this large amount of data. Everything is in the instance, and different examples carry different data. The possibility of creating multiple examples and distinguishing their data does not exist in the "global variables" or "freezing variables" method. The "cargo" method can handle this problem, but for the extension example, we can see that it may be necessary to start passing various values. Now that we have mentioned this, it is interesting to know how to handle the OPP style of message passing (Smalltalk or Self is similar to this, as are some of the OOP xBase variables I use ):
Listing 8. tax calculation of Smalltalk (Python)
class TaxCalc: deftaxdue (self): return (self.income-self.deduct)*self.rate defsetIncome (self,income): self.income = income return self defsetDeduct (self,deduct): self.deduct = deduct return self defsetRate (self,rate): self.rate = rate return self print "Smalltalk-style taxes due =", \ TaxCalc().setIncome(50000).setRate(0.30).setDeduct(10000).taxdue()
Every "setter" is used to return self so that we can regard "existing" as the result of every method application. This has many interesting similarities with the FP closure method.
With the Xoltar toolkit, we can create a complete closure with the expected features of merging data and functions, and also allow multipart closure (nee object) to contain different packages:
Listing 9. Python function-style tax calculation
from functional import *taxdue = lambda : (income-deduct)*rateincomeClosure = lambda income,taxdue: closure(taxdue)deductClosure = lambda deduct,taxdue: closure(taxdue)rateClosure = lambda rate,taxdue: closure(taxdue)taxFP = taxduetaxFP = incomeClosure(50000,taxFP)taxFP = rateClosure(0.30,taxFP)taxFP = deductClosure(10000,taxFP) print "Functional taxes due =",taxFP() print "Lisp-style taxes due =", \ incomeClosure(50000, rateClosure(0.30, deductClosure(10000, taxdue)))()
Each closure function we define carries any value defined within the function range, and then binds these values to the global scope of the function object. However, the global range of a function does not seem to be the same as the global range of the actual module, and it is also different from the "global" range of different closures. Closures simply "carry data ".
In the example, we use some special functions to put specific bindings in the closure range (income, deduct, rate. It is also easy to modify the design to put any binding within the scope. We can also use different function styles with nuances in the example. of course, this is just for fun. The first one successfully binds the added value to the closure range, and makes the taxFP variable. The Rows "added to the closure" can appear in any order. However, if you want to use an unchangeable name such as tax_with_Income, you must sort the bound rows in a certain order and then pass the previous binding to the next one. In any case, once everything necessary is bound to the closure range, we call the "seeded" function.
The second style looks closer to Lisp (more like parentheses for me ). If you do not consider appearance, there are two interesting things in the second style. First, name binding is completely avoided. The second style is a single expression without the use of statements (see section 1st to discuss why this problem occurs ).
Other interesting examples of the use of "Lisp style" closures are similar to the Smalltalk style message passing method mentioned above. The two accumulate values and call the taxdue () function/method (if there is no correct data, both will report errors in these original versions ). The "Smalltalk style" transmits objects between each step, while the "Lisp style" delivers a continuity. But for a deeper understanding, most functions and object-oriented programming are like this.