Some features of in-depth Python function programming

Source: Internet
Author: User
binding

The attentive reader may recall the limitations I pointed out in the 1th part of the function technique. It is not possible to avoid the rebinding of names that represent function expressions, especially in Python. In FP, names are often interpreted as abbreviations for longer expressions, but this implies that "the same expression always evaluates the same value". This hint is not true if the name of the tag is re-bound. For example, let's define some quick expressions to use in function programming, such as:
Listing 1. The following Python FP section is rebinding to cause a failure

>>> car =         Lambda         lst:lst[0]>>> cdr =         lambda         lst:lst[1:]>>> sum2 =         Lambda         lst:car (LST) +car (CDR) >>> sum2 (range) 1>>> car =         Lambda         lst:lst[2] >>> sum2 (Range (10)) 5

Unfortunately, the exact same expression, sum2 (range (10)), evaluates two different values in two places in the program, even though the expression itself does not use any mutable variables in its arguments.

Fortunately, the functional module provides classes called Bindings (proposed to Keller) to prevent such rebinding (at least in the occasional case, Python does not block the programmer who is bent to unbind). However, using Bindings requires some extra syntax, which makes it less prone to accidents. In the example of the functional module, Keller named the Bindings instance let (which I assume is followed by an ML family language, which is given the word). For example, we will do this:
Listing 2. Python FP section 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) 0
 
  

Obviously, the real program has to do some setup to catch "binding errors", and they are thrown away to avoid a class of problems.

Together with Bindings, functional provides the namespace function to get the namespace from the Bindings instance (which is actually a dictionary). This is very easy to implement if you want to operate an expression in a (immutable) namespace defined in Bindings. The eval () function of Python allows operations in the namespace. Let's use an example to figure out:
Listing 3. Python FP section with immutable namespaces

>>> let = Bindings ()           # "Real World" function names>>> Let.r10 = range (Ten) >>> 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>>& Gt Inv.car =         Lambda         lst:lst[-1]>>> inv.cdr =         Lambda         lst:lst[:-1]>>> eval (' Car (R10) + Car (CDR (R10)) ', Namespace (INV)) 17

Closed Package

There is an interesting concept in FP-closures. In fact, closures are very interesting to many developers, including closures, even in non-functional languages such as Perl and Ruby. Also, Python 2.1 is currently trying to incorporate the lexical scoping feature, which will provide most of the functionality of closures.

What is a closure? Steve Majewski recently provided a good description of the concept in the Python newsgroup:

The object is the data that accompanies the process ... Closures are the process of accompanying data.

Closures are like FP's Jekyll for OOP Hyde (roles may also be swapped). Closures like object examples are a way to encapsulate a large number of data and functionality together.

Let's go back to where we were before we knew what the object and closure were going to do, and how the problem would be solved without it. The result returned by a function is often determined by the context used in its calculation. The most common--and perhaps most obvious--method of specifying the context is to pass some parameters to the function, notifying the function of what value to process. But sometimes the "background" and "foreground" parameters are fundamentally different-the difference between what the function is processing at this particular moment and the "configuration" of the function as a multiple-segment potential call.

There are many ways to deal with the background when the focus is on the foreground. One of these is the simple "bite-bullet" method, which passes every parameter required by the function at each invocation. This method is usually used in the call chain, as long as the value is likely to be needed in some places, and some values (or structures with multiple members) are passed. Here is a small example:
Listing 4. Displays the Python portion 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 (Ten)           # Pass Cargo value for use DOWNSTREAM17

in B () of the cargo example, N has no effect other than the act of passing to C (). Another method will use global variables:
Listing 5. Display the Python portion of a global variable

>>> N = 10>>>         defaddn        (i): ...           Global         N           ... return         i+n...>>> Addn (7)          # Add global N to argument17>>> n = 20>>> addn (6)          # Add GL Obal N to argument26 global variable n plays a role in any call to Addn (), but there is no need to explicitly pass the global background "context". Another technique that is more Python-specific is to "freeze" a variable when it is defined into a function that uses default parameters: Listing 6. Python section showing frozen variables >>> N = 10>>>         defaddn        (i, n=n): ...           return         i+n...>>> Addn (5)          # add 1015>>> n = 20>>> addn (6)          # Add ten (current n Doe) SN ' t matter) 16

A frozen variable is essentially a closed packet. Some data is "subordinate" to the ADDN () function. For a complete closure, when the ADDN () is defined, all data will be available at the time of the call. However, in this example (or many more robust examples), the default parameters can be used simply enough. ADDN () A variable that has never been used has no effect on its calculation.

Then let's look at an OOP approach that is closer to the real problem. The time of year is that I think of the "meet" style of collecting the various data of the tax program--not in a particular order--end up using all the data to calculate. 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 =", tax Class.taxdue ()

In the TaxCalc class (or its instances), you can collect some data--in any order--once you get all the elements you need, you can call this object's method to complete the calculation of this large number of data. Everything is in the instance, and the different examples carry different data. The possibility of creating multiple samples and distinguishing their data cannot exist in the global variables or frozen variable methods. The "cargo" method can handle this problem, but for an extended example, we see that it may be necessary to start passing various values. Now that we've talked about it, it's also interesting to note how the OPP style of passing messages is handled (Smalltalk or self is similar to this, some of the OOP xBase variables I use):
Listing 8. Smalltalk Style (Python) tax calculation

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 ()

Returning self with each "setter" allows us to think of "existing" things as the result of each method application. This has many interesting similarities with the FP closure approach.

With the Xoltar Toolkit, we can create a complete closure with the desired combined data and function characteristics, while also allowing a multi-segment closure (nee Object) to contain different packages:
Listing 9. The tax calculation of Python function style

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 that we define carries the values defined in the function scope and binds the values to the global scope of the function object. However, the global scope of the function does not appear to be the same as the global scope of the actual module, and is not the same as the global scope of the different closures. Closures are simply "carrying data".

In the example, we used some special functions to put a specific binding within the closure range (income, deduct, rate). It is also easy to modify the design to put any bindings within the scope. We can also use different function styles with subtle differences in the example, which is just for fun, of course. The first succeeds in binding the added value into the closure scope; making the TAXFP variable, these "join to closure" rows can appear in any order. However, if you want to use immutable names such as Tax_with_income, you must arrange the bound rows in a certain order and then pass the preceding bindings to the next. In any case, once the required everything is bound into the scope of the closure, we call the "seeded" function.

The second style looks closer to Lisp (more like parentheses to me). If you don't consider aesthetics, there are two interesting things in the second style. The first thing is that the name bindings are completely avoided. The second style is a single expression without using a statement (see part 1th for a discussion of why this is problematic).

Other interesting examples of the use of "Lisp style" closures are how much they are similar to the "Smalltalk style" message passing method mentioned above. Both accumulate values and call the Taxdue () function/method (both will have an error in these original versions if there is no correct data). "Smalltalk style" passes objects between each step, while "Lisp style" passes a continuous. But for a deeper understanding, functions and object-oriented programming are mostly like this.

  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.