我学过多门编程语言,却变得越来越迷惑。我们知道C语言,每个变量都需要声明变量类型,在函数调用的时候也必须保证参数类型一致。而python 的变量不需要声明类型,且甚至不许要提前声明,python极大的降低了程序开发的门槛(牺牲性能换来的)。本文着重解决2个问题:
本文借由第一个问题探究python语言设计的底层实现,借由第二个问题探究python语言设计时的高层设计模式思想。
classmethod
和staticmethod
等语法来实现工厂设计模式。同时,还可以使用第三方库比如factory_boy
来实现工厂模式。Queue.Queue
、threading.Event
等。for ... in ...
循环语句就是基于迭代器模式实现的。Python 的内置类型如列表、元组、字典等都可以使用迭代器来遍历collections.Iterable
抽象基类就是一种适配器模式的实现,它可以将任意一个对象转换为可迭代的对象。具体来说,当我们导入一个模块时,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
类,并创建了两个对象s1
和s2
,我们可以通过s1 is s2
来判断这两个对象是否是同一个实例,如果是,则说明单例模式生效
装饰器模式可以用在很多地方,如计时器、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
,使得它在被调用前后能够自动输出一些信息。
classmethod和staticmethod
在Python中通过使用@classmethod
和@staticmethod
等语法来实现工厂设计模式,实际上是利用Python的装饰器语法来实现。装饰器的用法和原理上面已经讲过了。
在使用classmethod
和staticmethod
这两个装饰器时,我们可以用它们来定义类级别的方法,而不需要先创建一个实例。这些方法可以用来创建、初始化、返回对象等,这正是工厂模式所要完成的任务。
我们可以通过@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()
的抽象方法。ProductA
和ProductB
都是Product
的子类,它们分别实现了use()
方法。
然后,我们定义了一个名为Factory
的工厂类,它有一个名为create_product()
的类方法。这个方法根据传入的参数来创建不同种类的产品,并返回创建好的产品实例。如果传入的参数无效,我们将抛出一个ValueError
异常。
最后,我们通过调用Factory.create_product()
方法来创建不同的产品实例,并调用它们的use()
方法来演示其功能。
工厂模式可以帮助我们隐藏复杂的对象创建过程,同时提供一个简单的接口来创建不同的对象类型。在Python中,通过使用@classmethod
和@staticmethod
等语法,可以很方便地实现工厂模式。
策略模式是一种设计模式,它定义了一系列算法,将每个算法封装到具有共性的多个类中,使它们可以互相替换。在 Python 中,由于函数的动态特性,我们可以直接将实现了不同算法的函数作为不同的策略,然后将它们作为参数传入一个共同的处理函数中,从而实现同一组数据的不同处理方式。
例如,假设我们需要对一组数字进行排序,我们可以定义一个函数sort(numbers, strategy)
,其中numbers
表示待排序的数字列表,strategy
表示排序策略,可以是冒泡排序、快速排序、归并排序等等。我们可以定义多个不同的排序函数,然后将它们作为参数传递到sort
函数中,从而实现不同的排序策略。
这样做的好处是,我们可以根据需要动态地选择不同的排序策略,而不需要修改sort
函数的实现。同时,我们也可以进一步抽象出一个排序策略的接口,以便于更加灵活地替换策略函数。
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.Iterable
和collections.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文档中的相关内容。