python后端技术栈_Python后端技术栈(三)--设计模式
正文共:10260 字 5 圖
預計閱讀時間:26 分鐘
每日分享
Somewhere, something incredible is waiting to be known.
總有令人驚嘆的東西,在某處等著被發現。
小閆語錄:
生活好像一次尋寶,總有一些驚喜在未知處等著你,請不要沮喪,也不要放棄。你現在的平淡只是在做排除罷了。
1.3編程范式
上篇文章傳送門『我是個鏈接』
上篇文章對 Python 的一些數據結構和常用算法做了歸納概括,很多東西還需大家多多練習才能掌握,算法需要理解,而非記憶。
本篇文章將開始編程范式的相關內容,開始咯~
1.3.1面向對象基礎以及 Python 類
1.3.1.1什么是面向對象編程
Object Oriented Programming(OOP)
1.把對象作為基本單元,把對象抽象成類(Class),包含成員和方法。
2.三個特點:數據封裝、繼承和多態。多態:同一操作作用于不同的對象,可以有不同的解釋,產生不同的執行結果
3.Python 中使用類來實現面向對象的編程。之前我們是過程式編程以函數為基本單位。
1.3.1.2 Python 中如何創建類
我們簡單的定義一個類:class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def print_name(self):
print('my name is {}'.format(self.name))Person 這個類在初始化的時候,包含名字和年齡兩個屬性。同時類具有一個方法,可以輸出姓名。
注意:在類中屬性前加單下劃線表示私有屬性,不希望外界進行訪問。在類中兩個下劃線表示的是魔法方法。
1.3.1.3組合和繼承
優先使用組合而非繼承
1.組合是使用其他的類實例作為自己的一個屬性(Has a 關系)
2.子類繼承父類的屬性和方法(Is a 關系)
3.優先使用組合保持代碼簡單
1.3.1.4類變量和實例變量
區分類變量和實例變量
1.類變量由所有實例共享
2.實例變量由實例單獨享有,不同實例之間不影響
3.當我們需要在一個類的不同實例之間共享變量的時候,需要使用類變量。
下面我們舉一個簡單的例子:class Person:
Country = 'China' # 類變量
def __init__(self, name):
self.name = name # 實例變量
def print_name(self):
print(self.name)上述例子中,我們可以知道,每一個實例都擁有 Country 這個屬性,而他們的 name 則是自己獨有的,互不相同,取決于構造實例時的輸入。
1.3.1.5 classmethod/staticmethod 區別
它們經常被用作類方法的裝飾器。
1.都可以通過 Class.method() 的方式使用
2.classmethod 第一個參數是 cls,可以引用類變量。使用此裝飾器裝飾后,表明是類方法,可以通過實例對象和類對象去訪問;類方法還有一個用途就是可以對類屬性進行修改。
3.staticmethod 使用起來和普通函數一樣,只不過放在類里面去組織。使用此裝飾器裝飾后,表明是靜態方法,靜態方法不需要多定義參數,可以通過對象和類來訪問。
小總結:
1.從類方法和實例方法以及靜態方法的定義形式就可以看出來,類方法的第一個參數是類對象cls,那么通過cls引用的必定是類對象的屬性和方法; 2.實例方法的第一個參數是實例對象self,那么通過self引用的可能是類屬性、也有可能是實例屬性(這個需要具體分析),不過在存在相同名稱的類屬性和實例屬性的情況下,實例屬性優先級更高。 3.靜態方法中不需要額外定義參數,因此在靜態方法中引用類屬性的話,必須通過類實例對象來引用
1.3.1.6什么是元類?使用場景?
元類(Meta Class)是創建類的類
1.元類允許我們控制類的生成,比如修改類的屬性等等。
2.使用 type 來定義元類。
3.元類最常見的一個使用場景就是 ORM 框架。
我們使用例子說明一下:class Base:
pass
class Child:
pass
# 等價定義 注意 Base 后要加上逗號否則就不是 tuple 了
SameChild = type('Child', (Base,), {})
# 加上方法
class ChildWithMethod(Base):
bar = True
def hello(self):
print('hello')
def hello(self):
print('hello')
# 等價定義
ChildWithMethod = type(
'ChildWithMethod', (Base,), {'bar': True, 'hello': hello}
)
# 元類繼承自 type
class LowercaseMeta(type):
"""修改類的屬性名稱為小寫的元類"""
def __new__(mcs, name, bases, attrs):
lower_attrs = {}
for k, v in attrs.items():
# 排除magic method
if not k.startswith('__'):
lower_attrs[k.lower()] = v
else:
lower_attrs[k] = v
return type.__new__(mcs, name, bases, lower_attrs)
class LowercaseClass(metaclass=LowercaseMeta):
BAR = True
def HELLO(self):
print('hello')
# 你會發現“BAR”和“HELLO”都變成了小寫
print(dir(LowercaseClass))
# 用一個類的實例調用hello方法,我們修改了類定義時候的屬性名!
LowercaseClass().hello()類中 __new__用來生成實例, __init__初始化實例
1.3.2 Python 裝飾器
如果想給一個類擴充功能,可以使用組合和繼承。裝飾器可以在不修改原函數的基礎上增添一些新的功能。
1.3.2.1什么是裝飾器
Decorator
1.Python 中一切皆對象,函數也可以當做參數傳遞
2.裝飾器是接受函數作為參數,添加功能后返回一個新函數的函數(類)
3.Python 中通過 @ 使用裝飾器
1.3.2.2編寫一個記錄函數耗時的裝飾器import time
# 接受一個函數作為參數
def log_time(func):
def _log(*args, **kwargs):
beg = time.time()
res = func(*args, **kwargs)
print('use time:{}'.format(time.time()-beg))
return res
return _log
@log_time
def mysleep():
time.sleep(1)
mysleep()@ 代表的是裝飾器語法糖
我們是用裝飾器等價于下面的形式:newsleep = log_time(mysleep)
newsleep()
裝飾器本質上只是一個函數,只是它特殊在將函數作為參數進行接收,調用完原始函數之后返回一個新的函數。
1.3.2.3如何使用類編寫裝飾器import time
class LogTime:
def __call__(self, func):
def _log(*args, **kwargs):
beg = time.time()
res = func(*args, **kwargs)
print('use time: {}'.format(time.time()-beg))
return res
return _log
@LogTime()
def mysleep():
time.sleep(1)
mysleep()
1.3.2.4如何給裝飾器增加參數
我們可以使用類裝飾器比較方便實現裝飾器參數import time
class LogTime:
def __init__(self, use_int=False):
self.use_int = use_int
def __call__(self, func):
def _log(*args, **kwargs):
beg = time.time()
res = func(*args, **kwargs)
if self.use_int:
print('use time: {}'.format(
int(time.time()-beg))
)
else:
print('use time: {}'.format(
time.time() - beg)
)
return res
return _log
@LogTime(True)
def mysleep():
time.sleep(1)
mysleep()
1.3.3設計模式之創建型模式 Python 應用
在動態語言里面,設計模式關注的會偏少一點。不像在 Java 里面,會涉及到大量的設計模式。比如三種設計模式:創建型、結構型和行為型。之所以動態語言比如 Python 里面設計模式講的少,并不是說它不重要,而是動態語言自己已經實現了一些設計模式,比如迭代器模式,裝飾器模式 Python 本身就支持。
1.3.3.1常見創建型設計模式
工廠模式(Factory):解決對象創建問題。
構造模式(Builder):控制復雜對象的創建
原型模式(Prototype):通過原型的克隆創建新的實例
單例(Borg/Singleton):一個類只能創建同一個對象
對象池模式(Pool):預先分配同一類型的一組實例
惰性計算模式(Lazy Evaluation):延遲計算(Python 的 property)
1.3.3.2工廠模式
我們先來簡單的看一下工廠模式:
1.解決對象的創建問題
2.解耦對象的創建和使用
3.包括工廠方法和抽象工廠
下面我們先來展示一個工廠方法的例子:class DogToy:
def speak(self):
print("wang wang~~")
class CatToy:
def speak(self):
print("miao miao~~")
def toy_factory(toy_type):
if toy_type == 'dog':
return DogToy()
elif toy_type == 'cat':
return CatToy()學習設計模式的一個有效的方式就是自己嘗試寫個示例代碼來演示它。
1.3.3.3構造模式
下面我們再來簡單的理解一下什么是構造模式(Builder):
1.用來控制復雜對象的構造
2.創建和表示分離。比如你要買電腦,工廠模式直接給你需要的電腦。但是構造模式允許你自己定義電腦的配置,組裝完成后給你。
下面我們用一個書上的例子來解釋一下:class Computer:
def __init__(self, serial_number):
self.serial = serial_number
self.memory = None
self.hdd = None
self.gpu = None
def __str__(self):
info = ('Memory: {}GB'.format(self.memory),
'Hard Disk: {}GB'.format(self.hdd),
'Graphics Card: {}'.format(self.gpu))
return '\n'.join(info)
class ComputerBuilder:
def __init__(self):
self.computer = Computer('AG23385193')
def configure_memory(self, amount):
self.computer.memory = amount
def configure_hdd(self, amount):
self.computer.hdd = amount
def configure_gpu(self, gpu_model):
self.computer.gpu = gpu_model
class HardwareEngineer:
def __init__(self):
self.builder = None
def construct_computer(self, memory, hdd, gpu):
self.builder = ComputerBuilder()
[step for step in (self.builder.configure_memory(memory),
self.builder.configure_hdd(hdd),
self.builder.configure_gpu(gpu))]
@property
def computer(self):
return self.builder.computer
# 使用Builder,可以創建多個Builder類實現不同的組裝方式。
engineer = HardwareEngineer()
engineer.construct_computer(hdd=2048, memory=16, gpu='GeForce GTX 2080 Ti')
computer = engineer.computer
print(computer)
1.3.3.4原型模式
這種模式平時接觸的會比較少,此處不再用代碼進行演示,只簡單的進行說明即可。
1.通過克隆原型來創建新的實例
2.可以使用相同的原型,通過修改部分屬性來創建新的示例
3.用途:對于一些創建實例開銷比較高的地方可以用原型模式
1.3.3.5單例模式
單例模式是我們經常碰到的一種模式,在面試的過程中遇到的也會很多,大部分都會要求手寫代碼。所以此處要格外的進行注意。
單例模式就是一個類創建出來的對象都是同一個。其實我們經常接觸它,也許你不注意而已,比如 Python 的模塊其實就是單例的,只會導入一次。不論你在代碼里面 import 多少次,最后其實解釋器只導入一次。
下面我們使用共享同一個實例的方式來創建單例模式。class Singleton:
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
_instance = super().__new__(cls, *args, **kwargs)
cls._instance = _instance
return cls._instance
class MyClass(Singleton):
pass
c1 = MyClass()
c2 = MyClass()
assert c1 is c2
1.3.4設計模式之結構型模式 Python 應用
1.3.4.1常見結構型設計模式
裝飾器模式(Decorator):無需子類化擴展對象功能
代理模式(Proxy):把一個對象的操作代理到另一個對象
適配器模式(Adapter):通過一個間接層適配統一接口
外觀模式(Facade):簡化復雜對象的訪問問題
享元模式(Flyweight):通過對象復用(池)改善資源利用,比如連接池
Model-View-Controller(MVC):解耦展示邏輯和業務邏輯
1.3.4.2代理模式
什么是代理模式(Proxy)呢?
1.把一個對象的操作代理到另一個對象
2.這里又要提到我們之前實現的 Stack/Queue ,把操作代理到 deque
3.通常使用 has-a 組合關系之前我們實現的棧就用到了代理模式。還有一個常用的地方就是實現一些安全相關的東西。比如說我們有一個比較裸的接口,但是我們想去安全的訪問它,我們可以加一個代理間接層,在代理層做一些校驗的工作。
1.3.4.3適配器模式
什么是適配器模式(Adapter):
1.把不同對象的接口適配到同一個接口
2.想象一個多功能充電頭,可以給不同的電器充電,充當了適配器
3.當我們需要給不同的對象統一接口的時候可以使用適配器模式class Dog(object):
def __init__(self):
self.name = "Dog"
def bark(self):
return "woof!"
class Cat(object):
def __init__(self):
self.name = "Cat"
def meow(self):
return "meow!"
class Adapter:
def __init__(self, obj, **adapted_methods):
"""We set the adapted methods in the object's dict"""
self.obj = obj
self.__dict__.update(adapted_methods)
def __getattr__(self, attr):
"""All non-adapted calls are passed to the object"""
return getattr(self.obj, attr)
objects = []
dog = Dog()
objects.append(Adapter(dog, make_noise=dog.bark))
cat = Cat()
objects.append(Adapter(cat, make_noise=cat.meow))
for obj in objects:
print("A {0} goes {1}".format(obj.name, obj.make_noise()))
1.3.5設計模式之行為型模式 Python 應用
1.3.5.1.常見學習行為型設計模式
迭代器模式(Iterator):通過統一的接口迭代對象
觀察者模式(Observer):對象發生改變的時候,觀察者執行相應動作
策略模式(Strategy):針對不同規模輸入使用不同的策略
1.3.5.2迭代器模式
1.Python 內置對迭代器模式的支持
2.比如我們可以用 for 遍歷各種 Iterable 的數據類型
3.Python 里可以實現 __next__ 和 __iter__ 實現迭代器可迭代的對象只需實現 __iter__ 方法即可from collections import deque
class Stack(object):
def __init__(self):
self._deque = deque()
def push(self, value):
return self._deque.append(value)
def pop(self):
return self._deque.pop()
def empty(self):
return len(self._deque) == 0
def __iter__(self):
res = []
for i in self._deque:
res.append(i)
for i in reversed(res):
yield i
1.3.5.3觀察者模式
1.發布訂閱是一種最常用的實現方式訂閱者在每次發布者發布消息的時候就會收到通知,執行相應的動作。
2.發布訂閱用于解耦邏輯
3.可以通過回調等方式實現,當發生事件時,調用相應的回調函數
我們下面用一個簡單的 demo 演示:class Publisher: # 發布者
def __init__(self):
self.observers = [] # 觀察者
def add(self, observer):
# 加入觀察者
if observer not in self.observers:
self.observers.append(observer)
else:
print('Failed to add: {}'.format(observer))
def remove(self, observer):
# 移除觀察者
try:
self.observers.remove(observer)
except ValueError:
print('Failed to remove: {}'.format(observer))
def notify(self):
# 調用觀察者的回調
[o.notify_by(self) for o in self.observers]
class Formatter(Publisher):
def __init__(self, name):
super().__init__()
self.name = name
self._data = 0
@property
def data(self):
return self._data
@data.setter
def data(self, new_value):
self._data = int(new_value)
# data在被合法賦值以后會執行notify
self.notify()
class BinaryFormatter:
"""訂閱者"""
def notify_by(self, publisher):
print("{}: '{}' has now bin data = {}".format(
type(self).__name__,
publisher.name,
bin(publisher.data)
))
# 發布者
df = Formatter('formatter')
# 訂閱者
bf = BinaryFormatter()
df.add(bf)
# 設置的時候調用訂閱者的notify_by
df.data = 3
1.3.5.4策略模式
策略模式(Strategy)是根據不同的輸入采用不同的策略,比如買東西超過 10 個 8 折,超過 20 個打 7 折。對外暴露統一的接口,內部采用不同的策略計算。
就上面提到的例子我們用代碼作以實現:class Order:
def __init__(self, price, discount_strategy=None):
self.price = price
self.discount_strategy = discount_strategy
def price_after_discount(self):
if self.discount_strategy:
discount = self.discount_strategy(self)
else:
discount = 0
return self.price - discount
def __repr__(self):
fmt = ""
return fmt.format(
self.price, self.price_after_discount()
)
def ten_percent_discount(order):
return order.price * 0.10
def on_sale_discount(order):
return order.price * 0.25 + 20
def main():
order0 = Order(100)
order1 = Order(100, discount_strategy=ten_percent_discount)
order2 = Order(100, discount_strategy=on_sale_discount)
print(order0)
print(order1)
print(order2)
main()
1.3.6 Python 函數式編程
Python 支持部分函數式編程特性
1.把電腦的運算視作數學上的函數計算(lambda 演算)不要問是什么,一般人是涉及不到的,這是學術上的內容
2.高階函數: map/reduce/filter
3.無副作用,相同的參數調用始終產生同樣的結果
map函數的使用In [1]: map(lambda x:x*2, range(10))
Out[1]:
In [2]: list(map(lambda x:x*2, range(10)))
Out[2]: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
但是我們一般推薦列表推到代替 mapIn [3]: [ i*2 for i in range(10)]
Out[3]: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
reduce函數的使用In [5]: reduce(lambda x,y: x+y, range(1, 6))
Out[5]: 15
In [6]: 1+2+3+4+5
Out[6]: 15
filter函數的使用In [7]: list(filter(lambda x: x%2==0, range(10)))
Out[7]: [0, 2, 4, 6, 8]
In [8]: [i for i in range(10) if i % 2 == 0]
Out[8]: [0, 2, 4, 6, 8]
1.3.6.1閉包(Closure)
1.綁定了外部作用域的變量的函數
2.即使程序離開外部作用域,如果閉包仍然可見,綁定變量不會銷毀。
3.每次運行外部函數都會重新創建閉包閉包:引用了外部自由變量的函數
自由變量:不在當前函數定義的變量
特性:自由變量會和閉包函數同時存在
下面簡單的概括一下就是:在一個函數中定義了一個另外一個函數,內函數里運用了外函數的臨時變量,并且外函數的返回值是內函數的引用,這樣就構成了一個閉包。from functools import wraps
def cache(func):
store = {}
@wraps(func)
def _(n):
if n in store:
return store[n]
else:
res = func(n)
store[n] = res
return res
return _
@cache
def f(n):
if n <= 1:
return 1
return f(n-1) + f(n-2)
print(f(10))
優質文章推薦:
總結
以上是生活随笔為你收集整理的python后端技术栈_Python后端技术栈(三)--设计模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如果地球不毁灭于氦闪,那它将毁灭于什么
- 下一篇: websocket python爬虫_p