Python编程语言体现出的设计模式

引言

我学过多门编程语言,却变得越来越迷惑。我们知道C语言,每个变量都需要声明变量类型,在函数调用的时候也必须保证参数类型一致。而python 的变量不需要声明类型,且甚至不许要提前声明,python极大的降低了程序开发的门槛(牺牲性能换来的)。本文着重解决2个问题:

  • Python如何实现不需要声明数据类型
  • python语言本身体现了哪些设计模式?

本文借由第一个问题探究python语言设计的底层实现,借由第二个问题探究python语言设计时的高层设计模式思想。

二、Python语言本身体现了哪些设计模式?

  • 单例模式(Singleton Pattern):Python的模块就是一种单例模式,因为Python在导入模块时,会缓存已经导入的模块。因此,任何时候导入同一模块都会得到相同的实例。
  • 装饰器模式(Decorator Pattern):Python的装饰器是一种非常强大的模式,可以通过装饰器在不修改被装饰函数的情况下,添加新的功能。
  • 工厂模式(Factory Pattern):Python的工厂函数可以看做一种工厂模式,工厂函数接收参数,根据参数返回特定的实例对象。通过使用classmethodstaticmethod等语法来实现工厂设计模式。同时,还可以使用第三方库比如factory_boy来实现工厂模式。
  • 观察者模式(Observer Pattern):Python中的事件机制可以看做是一种观察者模式,当一个事件发生时,所有订阅该事件的观察者都会收到通知。Python 的很多标准库模块都采用了观察者模式的实现方式,例如Queue.Queuethreading.Event等。
  • 策略模式(Strategy Pattern):Python中的函数可以看做是一种策略模式的实现,不同的函数实现了不同的策略,在不同的场景下使用不同的策略函数。
  • 迭代器模式(Iterator Pattern):Python中的迭代器是一个非常常见的模式,可以对不同的数据结构进行统一的遍历操作。for ... in ... 循环语句就是基于迭代器模式实现的。Python 的内置类型如列表、元组、字典等都可以使用迭代器来遍历
  • 适配器模式(Adapter Pattern):Python的函数式编程特性可以看做是一种适配器模式的实现,通过函数的返回值进行适配,将不同的接口进行统一。Python 标准库的collections.Iterable抽象基类就是一种适配器模式的实现,它可以将任意一个对象转换为可迭代的对象。
  • 组合模式(Composite Pattern):Python 的内置类型中,列表和字典都是基于组合模式实现的。列表中的元素可以是任意类型的对象,包括其他列表,字典中的值也可以是任意类型的对象,包括其他字典。

单例模式(Singleton Pattern)

具体来说,当我们导入一个模块时,python会检查该模块是否已经被加载,如果没有被加载,python会加载并执行该模块的代码,并将该模块的命名空间加入sys.modules中,以便在所有引用该模块的地方都能够访问到该模块。

由于python只会导入一个模块的一份实例,所以我们可以在模块中定义一个类作为单例对象,如果该类已经被实例化过了,我们就直接返回该实例,否则就创建一个新的实例,并将其保存到类的一个类属性中,以便下次使用。

具体代码实现和示例如下所示:

# singleton.py
class Singleton:
    _instance = None
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
        
# main.py
from singleton import Singleton

s1 = Singleton()
s2 = Singleton()

assert s1 is s2 # True

在上面的示例中,我们定义了一个Singleton类作为单例对象,其中_instance是一个类属性,用于保存单例实例。在__new__方法中,我们首先判断该类的_instance属性是否为空,如果为空,就调用父类的__new__方法创建一个新的实例,否则直接返回已有的实例。

在主程序中,我们通过导入singleton模块来使用Singleton类,并创建了两个对象s1s2,我们可以通过s1 is s2来判断这两个对象是否是同一个实例,如果是,则说明单例模式生效

装饰器模式(Decorator Pattern)

装饰器模式可以用在很多地方,如计时器、log日志、权限控制、缓存、异常处理等。下面是一个简单的装饰器示例:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print('Before the function is called.')
        result = func(*args, **kwargs)
        print('After the function is called.')
        return result
    return wrapper

@my_decorator
def say_hello(name):
    print(f"Hello {
      
      name}!")
# #等价如下程序
# def say_hello(name):
#     print(f"Hello {name}!")
# say_hello = my_decorator(say_hello)

say_hello("Alice")

上述代码中,定义了一个装饰器my_decorator,它接受一个函数func作为参数,并返回一个新的函数wrapper。在新函数中,先输出一句话表示函数被调用之前,然后执行原函数,并获取返回结果。最后再输出一句话表示函数被调用之后,并返回原函数的返回结果。

通过在函数定义前加上@my_decorator,就能够装饰函数say_hello,使得它在被调用前后能够自动输出一些信息。

工厂模式(Factory Pattern)

classmethod和staticmethod

在Python中通过使用@classmethod@staticmethod等语法来实现工厂设计模式,实际上是利用Python的装饰器语法来实现。装饰器的用法和原理上面已经讲过了。

在使用classmethodstaticmethod这两个装饰器时,我们可以用它们来定义类级别的方法,而不需要先创建一个实例。这些方法可以用来创建、初始化、返回对象等,这正是工厂模式所要完成的任务。

我们可以通过@staticmethod装饰器来定义一个静态方法,该方法不需要实例化对象,可以直接通过类名来调用。静态方法通常用来处理独立于对象状态的操作。例如:

class MyClass:
    @staticmethod
    def my_staticmethod():
        print("This is a static method")

MyClass.my_staticmethod()  # This is a static method

我们也可以通过@classmethod装饰器来定义一个类方法,该方法的第一个参数是类本身,通常被命名为cls。类方法可以在不实例化对象的情况下操作类和对象。例如:

class MyClass:
    count = 0

    @classmethod
    def increase_count(cls):
        cls.count += 1

MyClass.increase_count()
MyClass.increase_count()
print(MyClass.count)  # 2

上述示例中,我们定义了一个名为increase_count的类方法,它通过cls参数来操作类属性count,从而实现对该属性的累加。我们可以直接使用类名调用这个类方法来进行操作。

例子

工厂模式通常包含两个部分:工厂和产品。工厂是一个负责创建产品的类或函数,它可以接收参数来决定要创建的产品类型。产品是被创建出来的实例对象,它们都有共同的接口或基类,从而保证了客户端代码可以统一调用不同种类的产品。

在Python中,我们可以定义一个名为Product的基类,然后在它的子类中定义不同种类的产品,并在一个名为Factory的工厂类中实现根据参数创建对应产品的逻辑。

下面是一个简单的示例代码:

class Product:
    def use(self):
        pass


class ProductA(Product):
    def use(self):
        print("Product A is being used.")


class ProductB(Product):
    def use(self):
        print("Product B is being used.")


class Factory:
    @classmethod
    def create_product(cls, product_type):
        if product_type == "A":
            return ProductA()
        elif product_type == "B":
            return ProductB()
        else:
            raise ValueError("Invalid product type")


product_a = Factory.create_product("A")
product_a.use()  # Output: Product A is being used.

product_b = Factory.create_product("B")
product_b.use()  # Output: Product B is being used.

上述代码中,我们定义了一个名为Product的基类,它包含一个名为use()的抽象方法。ProductAProductB都是Product的子类,它们分别实现了use()方法。

然后,我们定义了一个名为Factory的工厂类,它有一个名为create_product()的类方法。这个方法根据传入的参数来创建不同种类的产品,并返回创建好的产品实例。如果传入的参数无效,我们将抛出一个ValueError异常。

最后,我们通过调用Factory.create_product()方法来创建不同的产品实例,并调用它们的use()方法来演示其功能。

工厂模式可以帮助我们隐藏复杂的对象创建过程,同时提供一个简单的接口来创建不同的对象类型。在Python中,通过使用@classmethod@staticmethod等语法,可以很方便地实现工厂模式。

策略模式(Strategy Pattern)

策略模式是一种设计模式,它定义了一系列算法,将每个算法封装到具有共性的多个类中,使它们可以互相替换。在 Python 中,由于函数的动态特性,我们可以直接将实现了不同算法的函数作为不同的策略,然后将它们作为参数传入一个共同的处理函数中,从而实现同一组数据的不同处理方式。

例如,假设我们需要对一组数字进行排序,我们可以定义一个函数sort(numbers, strategy),其中numbers表示待排序的数字列表,strategy表示排序策略,可以是冒泡排序、快速排序、归并排序等等。我们可以定义多个不同的排序函数,然后将它们作为参数传递到sort函数中,从而实现不同的排序策略。

这样做的好处是,我们可以根据需要动态地选择不同的排序策略,而不需要修改sort函数的实现。同时,我们也可以进一步抽象出一个排序策略的接口,以便于更加灵活地替换策略函数。

迭代器模式(Iterator Pattern)

Python 中的迭代器模式体现在迭代器对象(Iterator Object)上。迭代器对象是用于遍历集合或序列(如列表、元组、字典等)的一种对象,在遍历时,它可以记录当前遍历的位置,以便于在下一次调用时继续遍历。

在 Python 中,我们可以通过内置函数iter()next()来获取迭代器对象并进行一次次的遍历操作,这就是迭代器模式的实现之一。

让我们通过一个简单的例子来说明如何使用Python迭代器模式。假设我们有一个列表,要遍历其中所有的元素:

my_list = [1, 2, 3, 4, 5]

# 通过 iter() 函数获取迭代器对象
my_iterator = iter(my_list)

# 通过 next() 函数遍历迭代器对象中的元素
print(next(my_iterator)) # 1
print(next(my_iterator)) # 2
print(next(my_iterator)) # 3
print(next(my_iterator)) # 4
print(next(my_iterator)) # 5

# 当所有元素遍历完毕后,再次调用next()函数将抛出异常
print(next(my_iterator)) # StopIteration

上面的代码中,我们首先使用iter()函数获取了一个迭代器对象my_iterator,然后使用next()函数遍历这个迭代器对象中的元素。在每次调用next()函数时,迭代器对象会返回下一个元素的值,直到所有元素都被遍历完毕。当所有元素遍历完毕后,再次调用next()函数将抛出一个StopIteration异常,表示迭代已经结束。

另外,Python 还通过抽象基类(Abstract Base Class)collections.abc.Iterablecollections.abc.Iterator来规范了迭代器的实现方式。这些抽象基类提供了通用的接口,可以保证实现该接口的对象是可迭代的,并且能够被内置函数iter()使用。

使用迭代器模式的好处是,我们可以使用相同的方式遍历不同的集合或序列,而且遍历方式也可以随时改变,而不需要修改集合或序列的实现。例如,我们可以使用同样的方式遍历一个列表、一个字符串和一个文件,这样就可以使代码更加通用和灵活。

我们也可以动手自己实现一个迭代器:

class MyIterator:
    def __init__(self, data):
        self.index = 0
        self.data = data

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.data):
            raise StopIteration
        result = self.data[self.index]
        self.index += 1
        return result

上面的代码中,我们定义了一个名为MyIterator的迭代器类,它接受一个列表作为参数。在__init__()方法中,我们初始化了index属性和data属性,分别表示当前遍历的位置和要遍历的数据。

__iter__()方法中,我们返回了迭代器对象自身,这是迭代器模式中的典型实现方式。

最后,在__next__()方法中,我们实现了迭代器的核心逻辑。首先,我们检查当前遍历的位置是否大于等于数据的长度。如果是,则抛出一个StopIteration异常,表示迭代结束。如果不是,则返回当前位置的元素,并将位置向后移动一位。

现在,我们可以使用这个迭代器类来遍历一个列表:

my_list = [1, 2, 3, 4, 5]
my_iterator = MyIterator(my_list)

for item in my_iterator:
    print(item)

输出结果为:

1
2
3
4
5

这个例子虽然简单,但是它显示了迭代器模式的核心思想:通过定义一个单独的迭代器类,我们可以在不修改原始数据的情况下,灵活地遍历数据,并且可以使用相同的方式遍历不同的数据类型。如果你想要深入了解如何实现更复杂的迭代器类,可以查看Python文档中的相关内容。

猜你喜欢

转载自blog.csdn.net/weixin_41099712/article/details/129974339
今日推荐