python函数式编程

一、函数式编程

函数式编程是一种编程风格,顾名思义,基于函数

1.高阶函数

def apply_twice(fun, arg):
    return fun(fun(arg))


def add_five(x):
    return x + 5


print(apply_twice(add_five, 10))

2.纯函数

函数式编程试图使用纯函数。纯函数没有副作用,并且返回一个仅依赖于它们的参数的值
纯函数example:

def pure_fun(x,y):
	temp = x + 2 * y
	return temp / (2 * x + y)

非纯函数的例子

some_list = []
def impure(arg):
	some_list.append(arg)

因为他改变了some_list的状态
纯函数优缺点:

  • 更容易推理和测试

  • 高效

  • 更容易并行运行

  • 一旦函数有一个输入,结果可以被存储在下一次需要该输入的函数时被引用,从而减少该函数被调用次数
    缺点

  • 使I/O的其他简单任务复杂化

  • 难以编写

3.lambda表达式

正常创建一个函数会自动分配给一个变量
这里与其他对象(如字符串和整数)的创建不同,他们可以在运行中被创建,而不必将其分配给变量
使用lambda语法创建函数,称为匿名函数
将一个简单函数作为参数传递给另一个函数时,这种方法是最常用的

语法如下lambda 参数列表:表达式
example

def my_fun(f,arg):
	return f(arg)
my_fun(lambda x:2*x*x,5)

lambda函数不如命名函数强大,他们只能做一些简单的事情,通常相当于一行代码

4.内置函数map和filter

def add_five(x):
  return x + 5

nums = [11, 22, 33, 44, 55]
result = list(map(add_five, nums))
print(result)

通过使用lambda语法,我们可以更加容易的获得相同结果

nums = [11, 22, 33, 44, 55]

result = list(map(lambda x: x+5, nums))
print(result)

filter函数通过删除与谓词不匹配的项来过滤一个迭代
filter(function,iterable)

nums = [11,22,33,44,55]
res = list(filter(lambda x: x%2==0,nums))
print(res)
与 map 一样,如果要打印结果,必须将结果显式转换为列表。

5.生成器

生成器是一种可迭代的类型,如列表或元组
生成器不能用索引,可以用for循环遍历
可以使用函数和yield创建

def countdown()
	i = 5
	while i > 0
		yield i
		i -= 1
for i in countdown()
	print(i)

6.装饰器

装饰圈是修改其他函数的功能的函数。装饰器有助于让我们的代码更加简短

def decor(func):
	def wrap():
		print('===========')
		func()
		print('===========')
		return wrap
def print_text():
	print('hello,world')

decoration = decor(print_text)
decoration()

Python通过预先用装饰器名称和 @symbol 预定义函数定义来提供支持,以便在装饰器中包装函数。
如果我们正在定义一个函数,我们可以使用@符号来“装饰”它

def decor(func):
  def wrap():
    print("============")
    func()
    print("============")
  return wrap

@decor
def print_text():
  print("Hello world!")

print_text()

7.递归

递归是函数式编程中一个非常重要的概念。

递归的基本部分是自引用 - 调用自己的函数。它被用来解决可以被分解成相同类型的更容易的子问题的问题。
一个递归实现的函数的典型例子是阶乘函数,N的阶乘写作N!表示小于等于N的所有正整数的乘积。

例如,5! (5的阶乘)是 5 * 4 * 3 * 2 * 1(120)。可以这样拆解然后用递归实现, 5! = 5 * 4!,4! = 4 * 3!,3! = 3 * 2! 等等。一般来说,n! = n * (n-1)!,然而,1! = 1 被称为基准情形(base case),因为它可以被直接计算,而不用执行更多的阶乘因子。
调用自身、结束条件

def factorial(x):
  if x == 1:
    return 1
  else: 
    return x * factorial(x-1)
    
print(factorial(5))

RuntimeError: maximum recursion depth exceeded
递归也可以是间接的。一个函数可以调用第二个函数,第二个函数再调用第一个函数,依此类推。这可以发生在任何数量的函数上。
例如:

def is_even(x):
  if x == 0:
    return True
  else:
    return is_odd(x-1)

def is_odd(x):
  return not is_even(x)


print(is_odd(17))
print(is_even(23))

8.set集合

集合在几个方面不同于列表,但共享几个列表操作,如 len。

集合是无序的,这意味着他们不能被索引。

集合不能包含重复的元素。

由于存储的方式,检查一个项目是否是一个集合的一部分比检查是不是列表的一部分更快。

集合使用 add 添加元素,而不是使用 append 。

remove 方法从集合中删除特定的元素; pop 删除随机的元素。

nums = {1, 2, 1, 3, 1, 4, 5, 6}
print(nums)
nums.add(-7)
nums.remove(3)
print(nums)

通常使用集合来消除重复的条目。
集合可以使用数学运算进行组合。

联合运算符 | 结合两个集合形成一个包含两个集合任一项目的新集合。

相交运算符& 获得两个集合共有的项目

差运算符 - 获取第一集合中的项目,但不是第二集合中的项目。

对称差分运算符^ 获取任集合中非共有的项目。

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)
print(first & second)
print(first - second)
print(second - first)
print(first ^ second)
{1, 2, 3, 4, 5, 6, 7, 8, 9}
{4, 5, 6}
{1, 2, 3}
{8, 9, 7}
{1, 2, 3, 7, 8, 9}

正如我们在前面的课程中所看到的,Python支持以下数据结构:列表,字典,元组,集合。

何时使用字典:

  • 当您需要键:值对之间的逻辑关联时。

  • 当您需要基于自定义密钥快速查找数据时。

  • 当你的数据不断修改时。请记住,字典是可变的。

何时使用其他类型:

  • 如果您有一些不需要随机访问的数据集合,请使用列表。当你需要一个简单的,可迭代的频繁修改的集合可以使用列表。

  • 如果你需要元素的唯一性,使用集合。

  • 当数据无法更改时使用元组。

很多时候,元组与字典结合使用,例如元组可能代表一个关键字,因为它是不可变的。

内置模块

tertools 模块

是一个标准库,包含了几个在函数式编程中很有用的函数。

1.一种类型的函数是无限迭代器。

1.count 函数从一个值无限增加。

2.cycle 函数无限次迭代(例如列表或字符串)。

3.repeat 函数重复一个对象,无论是无限还是特定的次数。

from itertools import count
for i in count(3):
  print(i)
  if i >=11:
    break
3 4 5 6 7 8 9 10 11
from itertools import cycle
for i in cycle(range(3)):
 print(i)
 if i == 2:
   print('循环结束‘)
   break
0 1 循环结束
from itertools import repeat
for i in repeat(range(3)):
print(i)

range(3)
range(3)
...
2.tertools 中有许多功能可以在迭代器上运行,类似于映射和过滤。

例如:

1.takewhile - 当判定函数(返回值为 True 或 False)保持为True时,从迭代中取得项目;

2.chain - 将几个迭代结合成一个长整数;

3.accumulate - 以可迭代的方式返回一个正在运行的值。

from itertools import accumulate, takewhile,chain

nums = list(accumulate(range(8)))
print(nums)
print(list(takewhile(lambda x: x<= 6, nums)))

a = [1,2,3,4]
b = ['a','b','c','d']
for each in chain(a,b)
	print(each)
[0, 1, 3, 6, 10, 15, 21, 28]
[0, 1, 3, 6]
3.itertool中还有几个组合函数

比如 product 和 permutation。

当你想用一些项目的所有可能的组合来完成任务时使用。

例如:

letters = ("A", "B")
print(list(product(letters, range(2))))
print(list(permutations(letters))) 
[('A', 0), ('A', 1), ('B', 0), ('B', 1)]
[('A', 'B'), ('B', 'A')]

二、面向对象编程

1.类

一个类可以被描述为一个对象的蓝图,描述或定义。

class Cat:
    def __init__(self, color, legs):
        self.color = color
        self.legs = legs
felix = Cat("ginger", 4)
rover = Cat("dog-colored", 4)
stumpy = Cat("brown", 3)

print("felix:",felix.__dict__) #dict是用来存储对象属性的一个字典,其键为属性名,值为属性的值.
print("rover:",rover.__dict__)
print("stumpy:",stumpy.__dict__)
1.1__init__ 方法是一个类中最重要的方法。

这是在创建类的实例(对象)时使用类名称作为函数调用的。
init 方法被称为类构造函数。

1.2方法

类可以定义方法来为其添加功能。

请记住,所有的方法必须有 self 作为他们的第一个参数。

这些方法使用与属性相同的点语法进行访问。

class Dog:
  def __init__(self, name, color):
    self.name = name
    self.color = color

  def bark(self):
    print("Woof!")

fido = Dog("Fido", "brown")
print(fido.name)
fido.bark()

尝试访问未定义实例的属性会导致 AttributeError。这也适用于你调用未定义的方法。

1.3继承

要从另一个类继承一个类,请将超类名放在类名后面的括号中。

class Animal: 
  def __init__(self, name, color):
    self.name = name
    self.color = color

class Cat(Animal):
  def purr(self):
    print("Purr...")
        
class Dog(Animal):
  def bark(self):
    print("Woof!")

fido = Dog("Fido", "brown")
print(fido.color)
fido.bark()

super 函数是一个与父类继承相关的函数。它可以用来在对象的超类中找到具有特定名称的方法。

class A:
  def spam(self):
    print(1)

class B(A):
  def spam(self):
    print(2)
    super().spam()
            
B().spam()

2 1

1.4 魔术方法

魔术方法是在名称的开始和结尾都有双下划线的特殊方法。
它们的一个常见用途是运算符重载。

class Vector2D:
  def __init__(self, x, y):
    self.x = x
    self.y = y
  def __add__(self, other):
    return Vector2D(self.x + other.x, self.y + other.y)

first = Vector2D(5, 7)
second = Vector2D(3, 9)
result = first + second
print(result.x)
print(result.y)
正如你所看到的,它添加了对象的相应属性并返回一个包含结果的新对象。
一旦定义了,我们就可以将这个类的两个对象相加。```
常见的魔术方法: 

__sub__ 对应 -
__mul__ 对应 *
__truediv__ 对应 /
__floordiv__ 对应 //
__mod__ 对应 %
__pow__ 对应 **
__and__ 对应 &
__xor__ 对应 ^
__or__ 对应 |
```py
class SpecialString:
  def __init__(self, cont):
    self.cont = cont

  def __truediv__(self, other):
    line = "=" * len(other.cont)
    return "\n".join([self.cont, line, other.cont])

spam = SpecialString("spam")
hello = SpecialString("Hello world!")
print(spam / hello)
============
Hello world!

Python 也为比较运算提供了魔术方法。

lt 对应 <
le 对应 <=
eq 对应 ==
ne 对应 !=
gt 对应 >
ge 对应 >=

class SpecialString:
  def __init__(self, cont):
    self.cont = cont

  def __gt__(self, other):
    for index in range(len(other.cont)+1):
      result = other.cont[:index] + ">" + self.cont
      result += ">" + other.cont[index:]
      print(result)

spam = SpecialString("spam")
eggs = SpecialString("eggs")
spam > eggs
e>spam>ggs
eg>spam>gs
egg>spam>s
eggs>spam>

如果 ne 没有被实现,它将返回 eq 相反的结果
有几个神奇的方法使类像容器一样行事。

len 对应 len()
getitem 对应 获取索引
setitem 对应 分配索引值
delitem 对应 删除索引值
iter 对应 迭代对象(例如for循环)
contains 对应 in

1.5对象生命周期

对象的生命周期由对象的创建,操作和销毁几个部分组成。

对象的生命周期

第一阶段是它所属的类的定义。

下一个阶段是调用 init 时实例的实例化。内存被分配来存储实例。

在调用 init 方法之前,Python首先调用 new 方法。

这之后,对象就可以使用了。

其他代码则可以通过调用对象和访问其属性来与对象交互。
最终,对象会完成使用,并可以被销毁。

当一个对象被销毁时,分配给它的内存被释放,并可用于其他目的。

当引用计数达到零时,就会发生对象的破坏。引用计数是引用一个对象的变量和其他元素的数量。

如果什么都没有引用它(它的引用计数为零),什么都不能与它交互,所以它可以安全地删除。

在某些情况下,两个(或更多)对象只能被彼此引用,因此也可以被删除。

del 语句会将对象的引用计数减少一个,这通常会导致删除。

del 语句的魔术方法是__del__。

不再需要的对象删除的过程称为垃圾收集。

总之,当一个对象的引用计数被分配一个新的名字或者放在一个容器(列表,元组或者字典)中时,它的引用计数会增加。

当用 del 删除对象的引用计数时,它的引用计数会减少,引用被重新分配或引用超出作用域。当一个对象的引用计数达到零时,Python 会自动删除它。

面向对象编程的一个关键部分是封装,它涉及将相关的变量和函数打包到一个简单易用的对象中 - 一个类的实例。

数据隐藏

1.一个相关的概念是数据隐藏,它指出一个类的实现细节应该被隐藏,并且为那些想要使用这个类的用户提供一个干净的标准接口。

在其他编程语言中,这通常使用私有方法和属性来完成,这些私有方法和属性阻止对类中某些方法和属性被外部访问。

Python 略有不同。没有对任何一个阶级的部分进行任意的限制。因此,没有办法强制一个方法或属性是严格私密的。

但是,有些方法可以阻止人们访问某个类的某些部分。
2.弱的私有方法和属性在开头只有一个下划线。

这表示它们是私有的,不应该被外部代码使用。但是,它大多只是一个约定,并不会阻止外部代码访问它们。

它唯一的实际效果是 from 模块名 import * 不会导入以单个下划线开头的变量。

class Queue:
    def __init__(self, contents):
        self._hidden_list = list(contents)

    def push(self, value):
        self._hidden_list.insert(0, value)

    def pop(self):
        self._hidden_list.pop(-1)

    def __repr__(self):
        return "Queue{}".format(self._hidden_list)


q = Queue([1, 2, 3])
print(q)
q.pop()
print(q)
q.push(10)
print(q)

私有方法和属性在名称的开始处有一个双下划线。这导致他们的名字错位,这意味着他们不能从类外访问。

这样做的目的不是确保它们保持私有,而是为了避免错误,如果存在具有相同名称的方法或属性的子类时。

名称 错位 方法仍然可以在外部访问,但是以不同的名称访问。 Spam 类的 __private 方法可以通过 _Spam__private 方法外部访问。

例如:

class Spam:
  __egg = 7
  def print_egg(self):
    print(self.__egg)

s = Spam()
s.print_egg()
print(s._Spam__egg)
print(s.__egg)

对象的方法,被类的实例所调用,传递方法的self参数。类方法是被类所调用的,传递参数cls,类方法用@classmethod标记

class Rectangle:
    def __init__(self, width, length):
        self.width = width
        self.length = length

    def calc_area(self):
        return self.length * self.width

    @classmethod
    def new_square(cls, squ):
        return cls(squ, squ)


a = Rectangle.new_square(5)
print(a.calc_area())

静态方法与类方法类似,只是它们没有任何额外的参数。

它们用 staticmethod 装饰器标记。

class Pizza:
  def __init__(self, toppings):
    self.toppings = toppings

  @staticmethod
  def validate_topping(topping):
    if topping == "pineapple":
      raise ValueError("No pineapples!")
    else:
      return True

ingredients = ["cheese", "onions", "spam"]
if all(Pizza.validate_topping(i) for i in ingredients):
  pizza = Pizza(ingredients)

除了可以从类的一个实例调用它们之外,静态方法的行为与纯函数类似。
属性提供了一种自定义实例属性访问的方法。

它们是通过将属性装饰器放在一个方法上面创建的,这意味着当访问与方法同名的实例属性时,方法将被调用。

属性的一种常见用法是使属性为只读。

class Pizza:
    def __init__(self, toppings):
        self.toppings = toppings

    @property
    def pizza_allowed(self):
        return False


p = Pizza('aaa')
print(p.pizza_allowed)  # False
p.pizza_allowed = True  # AttributeError: can't set attribute

属性也可以通过定义 setter/getter 函数来设置。

setter 函数设置相应的属性值。

getter 函数获取相应的属性值。

要定义一个 setter,你需要使用一个与属性相同名字的装饰器,后面跟着 .setter。

这同样适用于定义 getter 函数。

class Pizza:
    def __init__(self, toppings):
        self.toppings = toppings
        self._pizza_allowed = False

    @property
    def pizza_allowed(self):
        return self._pizza_allowed

    @pizza_allowed.setter
    def pizza_allowed(self,value):
        if value:
            password = input('请输入密码')
            if password == '123456':
                self._pizza_allowed = value
            else:
                raise ValueError('密码错误')


pizza = Pizza(["cheese", "tomato"])
print(pizza.pizza_allowed)
pizza.pizza_allowed = True
print(pizza.pizza_allowed)

使用面向函数编程

def get_input():
    word = input(":").split()  # split不传值默认以空格分割形成列表
    if word[0] in word_dict:
        verb = word_dict[word[0]]
    else:
        print('Unknown word {}'.format(word[0]))
        return

    if len(word) >= 2:
        noun_word = word[1]
        print(say(noun_word))
    else:
        say('nothing')


def say(sth):
    return 'you said {}'.format(sth)


word_dict = {
    'say': 'say'
}
get_input()

升级

def get_input():
    word = input(":").split()  # split不传值默认以空格分割形成列表
    if word[0] in word_dict:
        verb = word_dict[word[0]]
    else:
        print('Unknown word {}'.format(word[0]))
        return

    if len(word) >= 2:
        noun_word = word[1]
        print(verb(noun_word))
    else:
        verb('nothing')


class GameObjects:
    class_name = ''
    desc = ''
    object = {}

    def __init__(self, name):
        self.name = name
        GameObjects.object[self.class_name] = self
        print(self.class_name)

    def get_desc(self):
        return self.class_name + "\n" + self.desc


class Goblin(GameObjects):
    class_name = 'goblin'
    desc = 'a foul creature'


g = Goblin('goblin')


def say(sth):
    return 'you said {}'.format(sth)


def examine(none):
    if none in GameObjects.object:
        return GameObjects.object[none].get_desc()
    else:
        return 'there is no {} here'.format(none)


word_dict = {
    'say': say,
    'examine': examine
}
get_input()

使用面向对象编程

def get_input():
    word = input(":").split()  # split不传值默认以空格分割形成列表
    if word[0] in word_dict:
        verb = word_dict[word[0]]
    else:
        print('Unknown word {}'.format(word[0]))
        return

    if len(word) >= 2:
        noun_word = word[1]
        print(verb(noun_word))
    else:
        verb('nothing')


class GameObjects:
    class_name = ''
    desc = ''
    object = {}

    def __init__(self, name):
        self.name = name
        GameObjects.object[self.class_name] = self
        print(self.class_name)

    def get_desc(self):
        return self.class_name + "\n" + self.desc


class Goblin(GameObjects):
    def __init__(self, name):
        self.class_name = 'goblin'
        self._desc = 'a foul creature'
        self.health = 3
        super().__init__(name)

    @property
    def desc(self):
        if self.health >= 3:
            return self.health
        elif self.health == 2:
            health_line = 'you killed the knee'
        elif self.health == 1:
            health_line = 'you hit the arm'
        elif self.health <= 0:
            health_line = "it's dead"
        return self._desc + "\n" + health_line

    @desc.setter
    def desc(self, value):
        self._desc = value


g = Goblin('goblin')


def hit(noun):
    if noun in GameObjects.object:
        thing = GameObjects.object[noun]
        if type(thing) == Goblin:
            thing.health = thing.health - 1
            if thing.health <= 0:
                msg = "You killed the goblin!"
            else:
                msg = "You hit the {}".format(thing.class_name)
    else:
        msg = "There is no {} here.".format(noun)
    return msg


def say(sth):
    return 'you said {}'.format(sth)


def examine(none):
    if none in GameObjects.object:
        return GameObjects.object[none].get_desc()
    else:
        return 'there is no {} here'.format(none)


word_dict = {
    'say': say,
    'examine': examine,
    'hit':hit
}
while True:
    get_input()

你可能感兴趣的:(python函数式编程)