python 之【深入講解 RxPy 3.x 版本的重大改變】

這篇文章將詳細介紹 RxPy 的重大升級版本 RxPy 3.1.0

開篇:


之前我寫了一篇博客: python 之【超詳細講解 python 反應式編程】,介紹了什麼是反應式編程,並以 RxPy 1.6 版本爲基礎詳細地介紹了 RxPy 的使用方法。但隨着 RxPy 的不斷髮展和升級,RxPy 經歷了 2.x 版本並最終迎來了 3.x 的重大升級版本。3.x 版本對 Rx 的使用方式做出了很大改變,比如:

  • 操作符的鏈式操作改成了由 pip 操作符來集成構建
  • 可以自定義實現新的操作符
  • 操作鏈可以指定默認的調度器 scheduler
  • 通過補全支持更好地集成在 IDE 裏面



RxPy 3.x 的變化:


下面就通過例子來給大家講解一下,3.x 版本的操作帶來了哪些變化!(爲了方便理解,我會使用 v1 代替 RxPy 1.x 版本,使用 v3 表示 RxPy 3.x 版本

☕️ 更改了 Observable 對象的創建方式


在 v1 版本中創建 Observable 序列對象使用的 rx 裏面的 Observable 模塊來創建,比如:

from rx import Observable

def push_strings(observer):
    observer.on_next("Alpha")
    observer.on_next("Beta")

source = Observable.create(push_strings)

source.subscribe(lambda i: print("Received data: {0}".format(i)))

這裏的 create 操作符相當於 Observable 類的一個方法,但在 v3 版本中則不再使用 Observable 模塊來創建了,而是把這些工廠操作符封裝在 rx 模塊下一個個獨立的方法,然後可以直接獨立拿來使用,比如:

from rx import create

def push_strings(observer, scheduler):
    observer.on_next("Alpha")
    observer.on_next("Beta")

source = create(push_strings)

source.subscribe(lambda i: print("Received data: {0}".format(i)))

並且提供給 create 操作符的訂閱函數 push_strings 也變成了兩個參數了:一個 observer 對象和一個 scheduler 調度器。這個調度器參數是新實現的參數,如果在進行訂閱 subscribe() 的時候,提供了 scheduler 參數,則這個 scheduler 就會傳遞給 create 操作符的訂閱函數中,如果沒有提供則默認爲 None。


🍭 改變了鏈式操作符的組裝方式


在 v1 版本中,組裝鏈式操作符的方式爲一連串的點"."拼接操作,這些操作符的使用方式就像是調用 Observable 對象的一個方法一樣,比如:

from rx import Observable

Observable.of("Alpha", "Beta", "Gamma", "Delta", "Epsilon") \
    .map(lambda s: len(s)) \
    .filter(lambda i: i >= 5) \
    .subscribe(lambda value: print("Received {0}".format(value)))

但在 v3 版本中,則改成基於 pipe 操作符來進行鏈式組裝。並且 pipe 操作符已經成爲了 Observable 類的唯一方法了,換句話說,創建 Observable 對象的工廠操作符後面只能使用 pipe 這個操作符來進行數據的處理。

比如下面的例子:

import rx
from rx import operators

rx.of("Alpha", "Beta", "GammaRay").pipe(
    operators.map(lambda s: len(s)),
    operators.filter(lambda i: i >= 5)
).subscribe(lambda data: print("Received data: {0}".format(data)))

>>>>>
Received data: 5
Received data: 8

從這個例子中我們可以看出,v3 版本中,工廠操作符和非工廠操作符被劃分爲了兩個不同的模塊:

  • 工廠操作符(即創建 Observable 對象的操作符),比如:create、of、interval 等等被劃分到了 rx 模塊下,成爲了 rx 的一個獨立方法
  • 非工廠操作符(即處理 Observable 對象裏面的數據的操作符),比如:map、filter 等等被劃分到了 rx 下的 operators 模塊下,即這些操作符屬於 operators 模塊下的一個獨立方法

同時,從這個例子中我們也可以看出,非工廠操作符都封裝在了 pipe 操作符中,使用逗號分隔。這種方式帶來的改變是巨大的,正式 pipe 操作符的到來,使鏈式操作帶來了很大的便利,比如,我們可以自己使用函數來對 pipe 進行封裝:

import rx
from rx import operators

def filter_string_length():
    return rx.pipe(
        operators.map(lambda s: len(s)),
        operators.filter(lambda i: i >= 5)
    )

rx.of("Alpha", "Beta", "GammaRay").pipe(
    filter_string_length()
).subscribe(lambda data: print("Received data: {0}".format(data)))

>>>>>
Received data: 5
Received data: 8

從上面的例子可以看出,我們可以把多個操作符封裝成一個函數(實際上是把多個操作符包裝成一個獨立的 pipe 操作符),然後把函數直接放到 pipe 操作符當中。當然 pipe 中可以放入多個這種類型的函數,這就實現了我們所說的模塊化編程,這些都是得益於 pipe 操作符的強大之處。


📖 自定義新的操作符


在 v3 版本中,還允許我們自己創建一個新的操作符,允許我們完全控制訂閱立邏輯和數據項的發射邏輯。比如下面的例子:

import rx

def lowercase():
    # source 爲傳進來 Observable
    def observable_handle(source):
        def subscribe(observer, scheduler=None):
            # 重寫 on_next 來達到處理數據項的目的
            def on_next(value):
                observer.on_next(value.lower())

            return source.subscribe(
                on_next,    # 替換爲自己的 on_next
                observer.on_error,
                observer.on_completed,
                scheduler
            )
        # 返回一個自定義的新的 Observable
        return rx.create(subscribe)

    return observable_handle

rx.of("Alpha", "Beta", "Gamma", "Delta", "Epsilon").pipe(
    lowercase()
).subscribe(lambda data: print("Received data: {0}".format(data)))

>>>>>
Received data: alpha
Received data: beta
Received data: gamma
Received data: delta
Received data: epsilon

在這個例子當中我們自定義了一個將字符串變成小寫字母的操作符,自定義操作符是通過一個函數來實現的,這個函數需要返回一個處理 Observable 對象的函數句柄,比如 observable_handle 就是這樣一個句柄,它接收一個傳進來的 Observable 對象,然後返回一個新的自定義 Observable,在這個新的 Observable 中我們重寫了觀察者 oberver 的訂閱函數中的 on_next 方法來達到處理每個數據項的目的。(如果想要理解這個例子的原理,需要反覆多看幾遍


📝 移除了操作符中的數據項組合函數


在 v3 版本中,操作符裏面的組合多個不同 Observable 數據項的函數被移除了,比如 v1 版本中的 zip 操作符:

import operator
from rx import Observable

a = Observable.of(1, 2, 3)
b = Observable.of(1, 2, 3)

a.zip(b, lambda i, j: operator.mul(i, j)) \
    .subscribe(lambda data: print("Received data: {0}".format(data)))

>>>>>
Received data: 1
Received data: 4
Received data: 9

這裏的 zip 操作符使用了一個 lambda 函數組合了兩個 Observable 的數據。但在 v3 版本中則被移除了,改成了如下方式:

import operator
import rx
from rx import operators

a = rx.of(1, 2, 3)
b = rx.of(1, 2, 3)

a.pipe(
    operators.zip(b),   # 返回一個由 a 和 b 數據項組成的元組
    operators.map(lambda z: operator.mul(z[0], z[1]))
).subscribe(lambda data: print("Received data: {0}".format(data)))

>>>>>
Received data: 1
Received data: 4
Received data: 9

當然,例子中的 operators.map(lambda z: operator.mul(z[0], z[1])) 對元組數據的解包可以改成能夠自己解包元組數據的 starmap 操作符:operators.starmap(operator.mul),它們的結果都是一樣的。


🥚 移除了操作符中的 Observable 列表參數


在 v1 版本中,有一些操作符,比如:merge、zip、combine_latest 等等,把 Observable 列表作爲參數,然後把列表中的 Observable 進行組合:

from rx import Observable

obs1 = Observable.from_([1, 2])
obs2 = Observable.from_([3, 4])

res = Observable.merge([obs1, obs2])
res.subscribe(lambda data: print("Received data: {0}".format(data)))

>>>>>
Received data: 1
Received data: 2
Received data: 3
Received data: 4

在 v3 版本中則改成了可變參:

import rx

obs1 = rx.of(1, 2)
obs2 = rx.of(3, 4)

res = rx.merge(obs1, obs2)
res.subscribe(lambda data: print("Received data: {0}".format(data)))

>>>>>
Received data: 1
Received data: 2
Received data: 3
Received data: 4

☁️ 移除了阻塞 Observable


在 v1 版本中,我們通常會對 Observable 對象的數據進行阻塞,比如:

from rx import Observable

res = Observable.of(1, 2, 3, 4, 6).to_blocking().last()
print("Received last data: {0}".format(res))

>>>>>
Received last data: 6

這個例子中我們使用 to_blocking().last() 對 Observable 進行阻塞直到最後一個數據。在 v3 版本中,使用的是 run 操作符:

import rx

res = rx.of(1, 2, 3, 4, 6).run()
print("Received last data: {0}".format(res))

>>>>>
Received last data: 6

run 操作符會返回 Observable 發射的最後一個數據,當然還可以應用到其它阻塞操作符中:

  • obs.pipe(operations.first()).run()
  • obs.pipe(operations.to_list()).run()

☕️ 時間單位的更換


在 v1 版本中,把時間作爲單位的操作符,比如:interval、delay、debounce 等等,使用的是毫秒作爲單位。而在 v3 版本中變成了秒,比如:interval(1) 表示間隔爲1秒。


🍭 包名的更改


在 v3 版本中,對一些包進行了重命名,比如:

rx.concurrency >>> rx.scheduler
rx.disposables >>> rx.disposable
rx.subjects >>> rx.subject

同時,先前的包 rx.concurrency.mainloopscheduler 已經被分割成了兩部分:rx.scheduler.mainloop 和 rx.scheduler.eventloop



對於 RxPy 3.x 的版本就暫時介紹到這麼多,如果想要了解信息可以訪問官方文檔 RxPy Doc
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章