官术网_书友最值得收藏!

Functions as first-class objects

It shouldn't come as a surprise that Python functions are first-class objects. In Python, function objects have a number of attributes. The reference manual lists a number of special member names that apply to functions. Since functions are objects with attributes, we can extract the docstring or the name of a function, using special attributes such as __doc__ or __name__. We can also extract the body of the function through the __code__ attribute. In compiled languages, this introspection is relatively complex because of the source information that needs to be retained. In Python, it's quite simple.

We can assign functions to variables, pass functions as arguments, and return functions as values. We can easily use these techniques to write higher-order functions.

Additionally, a callable object helps us to create functions. We can consider the callable class definition as a higher-order function. We do need to be judicious in how we use the __init__() method of a callable object; we should avoid setting stateful class variables. One common application is to use an __init__() method to create objects that fit the Strategy design pattern.

A class following the Strategy design pattern depends on other objects to provide an algorithm or parts of an algorithm. This allows us to inject algorithmic details at runtime, rather than compiling the details into the class.

Here is an example of a callable object with an embedded Strategy object:

from typing import Callable
class Mersenne1:
    def __init__(self, algorithm : Callable[[int], int]) -> None:
        self.pow2 = algorithm
    def __call__(self, arg: int) -> int:
        return self.pow2(arg)-1  

This class uses __init__() to save a reference to another function, algorithm, as self.pow2. We're not creating any stateful instance variables; the value of self.pow2 isn't expected to change. The algorithm parameter has a type hint of Callable[[int], int], a function that takes an integer argument and returns an integer value.

The function given as a Strategy object must raise 2 to the given power. Three candidate objects that we can plug into this class are as follows:

def shifty(b: int) -> int:
    return 1 << b

def multy(b: int) -> int: if b == 0: return 1 return 2*multy(b-1)
def faster(b: int) -> int: if b == 0: return 1 if b%2 == 1: return 2*faster(b-1) t= faster(b//2) return t*t

The shifty() function raises 2 to the desired power using a left shift of the bits. The multy() function uses a naive recursive multiplication. The faster() function uses a divide and conquer strategy that will perform  multiplications instead of b multiplications.

All three of these functions have identical function signatures. Each of them can be summarized as Callable[[int], int], which matches the parameter, algorithm, of the Mersenne1.__init__() method.

We can create instances of our Mersenne1 class with an embedded strategy algorithm, as follows:

m1s = Mersenne1(shifty)
m1m = Mersenne1(multy)
m1f = Mersenne1(faster)  

This shows how we can define alternative functions that produce the same result but use different algorithms.

Python allows us to compute , since this doesn't even come close to the recursion limits in Python. This is quite a large prime number, as it has 27 digits.
主站蜘蛛池模板: 始兴县| 龙川县| 赤壁市| 积石山| 龙岩市| 南平市| 威海市| 昆明市| 清远市| 青冈县| 德清县| 肥乡县| 渭源县| 临高县| 淮阳县| 北安市| 仁怀市| 老河口市| 昌乐县| 赤峰市| 大关县| 鸡东县| 凯里市| 赤壁市| 宁安市| 信宜市| 隆化县| 民县| 贵州省| 樟树市| 延安市| 龙游县| 宿迁市| 仪征市| 永定县| 南岸区| 岑巩县| 镇坪县| 冷水江市| 宝鸡市| 喀喇|