Python進(jìn)階 - 命名空間與作用域

Python進(jìn)階 - 命名空間與作用域

寫在前面

如非特別說明,下文均基于Python3

命名空間與作用于跟名字的綁定相關(guān)性很大,可以結(jié)合另一篇介紹Python名字、對象及其綁定的文章眯分。

1. 命名空間

1.1 什么是命名空間

Namespace命名空間,也稱名字空間柒桑,是從名字到對象的映射弊决。Python中,大部分的命名空間都是由字典來實現(xiàn)的魁淳,但是本文的不會涉及命名空間的實現(xiàn)飘诗。

命名空間的一大作用是避免名字沖突:

def fun1():
    i = 1

def fun2():
    i = 2

同一個模塊中的兩個函數(shù)中,兩個同名名字i之間絕沒有任何關(guān)系界逛,因為它們分屬于不同明明空間昆稿。

1.2 命名空間的種類

常見的命名空間有:

  • built-in名字集合,包括像abs()這樣的函數(shù)息拜,以及內(nèi)置的異常名字等溉潭。通常,使用內(nèi)置這個詞表示這個命名空間-內(nèi)置命名空間

  • 模塊全局名字集合该溯,直接定義在模塊中的名字岛抄,如類,函數(shù)狈茉,導(dǎo)入的其他模塊等夫椭。通常,使用全局命名空間表示氯庆。

  • 函數(shù)調(diào)用過程中的名字集合蹭秋,函數(shù)中的參數(shù)扰付,函數(shù)體定義的名字等,在函數(shù)調(diào)用時被“激活”仁讨,構(gòu)成了一個命名空間羽莺。通常,使用局部命名空間表示洞豁。

  • 一個對象的屬性集合盐固,也構(gòu)成了一個命名空間。但通常使用objname.attrname的間接方式訪問屬性丈挟,而不是直接訪問刁卜,故不將其列入命名空間討論。

  • 類定義的命名空間曙咽,通常解釋器進(jìn)入類定義時蛔趴,即執(zhí)行到class ClassName:語句,會新建一個命名空間例朱。(見官方對類定義的說明)

1.3 命名空間的生命周期

不同類型的命名空間有不同的生命周期:

  • 內(nèi)置命名空間孝情,在Python解釋器啟動時創(chuàng)建,解釋器退出時銷毀洒嗤;

  • 全局命名空間箫荡,模塊的全局命名空間在模塊定義被解釋器讀入時創(chuàng)建,解釋器退出時銷毀渔隶;

  • 局部命名空間菲茬,這里要區(qū)分函數(shù)以及類定義。函數(shù)的局部命名空間派撕,在函數(shù)調(diào)用時創(chuàng)建,函數(shù)返回或者由未捕獲的異常時銷毀睬魂;類定義的命名空間终吼,在解釋器讀到類定義創(chuàng)建,類定義結(jié)束后銷毀氯哮。(關(guān)于類定義的命名空間际跪,在類定義結(jié)束后銷毀,但其實類對象就是這個命名空間內(nèi)容的包裝喉钢,見官方對類定義的說明)

2. 作用域

2.1 什么是作用域

作用域是Python的一塊文本區(qū)域姆打,這個區(qū)域中,命名空間可以被“直接訪問”肠虽。這里的直接訪問指的是試圖在命名空間中找到名字的絕對引用(非限定引用)幔戏。這里有必要解釋下直接引用間接引用

  • 直接引用;直接使用名字訪問的方式税课,如name闲延,這種方式嘗試在名字空間中搜索名字name痊剖。

  • 間接引用;使用形如objname.attrname的方式垒玲,即屬性引用陆馁,這種方式不會在命名空間中搜索名字attrname,而是搜索名字objname合愈,再訪問其屬性叮贩。

2.2 與命名空間的關(guān)系

現(xiàn)在,命名空間持有了名字佛析。作用域是Python的一塊文本區(qū)域益老,即一塊代碼區(qū)域,需要代碼區(qū)域引用名字(訪問變量)说莫,那么必然作用域與命名空間之間就有了聯(lián)系杨箭。

顧名思義,名字作用域就是名字可以影響到的代碼文本區(qū)域储狭,命名空間的作用域就是這個命名空間可以影響到的代碼文本區(qū)域互婿。那么也存在這樣一個代碼文本區(qū)域,多個命名空間可以影響到它辽狈。
作用域只是文本區(qū)域慈参,其定義是靜態(tài)的;而名字空間卻是動態(tài)的刮萌,只有隨著解釋器的執(zhí)行驮配,命名空間才會產(chǎn)生。那么着茸,在靜態(tài)的作用域中訪問動態(tài)命名空間中的名字壮锻,造成了作用域使用的動態(tài)性。

那么涮阔,可以這樣認(rèn)為:

靜態(tài)的作用域猜绣,是一個或多個命名空間按照一定規(guī)則疊加影響代碼區(qū)域;運行時動態(tài)的作用域敬特,是按照特定層次組合起來的命名空間掰邢。

在一定程度上,可以認(rèn)為動態(tài)的作用域就是命名空間伟阔。在后面的表述中辣之,我會把動態(tài)的作用域與其對應(yīng)命名空間等同起來。

2.3 名字搜索規(guī)則

在程序中引用了一個名字皱炉,Python是怎樣搜索到這個名字呢怀估?

在程序運行時,至少存在三個命名空間可以被直接訪問的作用域:

  • Local
    首先搜索娃承,包含局部名字的最內(nèi)層(innermost)作用域奏夫,如函數(shù)/方法/類的內(nèi)部局部作用域怕篷;

  • Enclosing
    根據(jù)嵌套層次從內(nèi)到外搜索,包含非局部(nonlocal)非全局(nonglobal)名字的任意封閉函數(shù)的作用域酗昼。如兩個嵌套的函數(shù)廊谓,內(nèi)層函數(shù)的作用域是局部作用域,外層函數(shù)作用域就是內(nèi)層函數(shù)的 Enclosing作用域麻削;

  • Global
    倒數(shù)第二次被搜索蒸痹,包含當(dāng)前模塊全局名字的作用域;

  • Built-in
    最后被搜索呛哟,包含內(nèi)建名字的最外層作用域叠荠。

程序運行時,LGB三個作用域是一定存在的扫责,E作用域不一定存在榛鼎;若程序是這樣的:

i = 1
print(i)

局部作用域在哪里呢?我們認(rèn)為(Python Scopes And Namespaces):

Usually, the local scope references the local names of the (textually) current function. Outside functions, the local scope references the same namespace as the global scope: the module’s namespace. Class definitions place yet another namespace in the local scope.

一般地鳖孤,局部作用域引用函數(shù)中定義的名字者娱。函數(shù)之外,局部作用域和全局作用域引用同一個命名空間:模塊的明星空間苏揣。然而類型的局部作用域引用了類定義新的命名空間黄鳍。

Python按照以上L-E-G-B的順序依次在四個作用域搜索名字。沒有搜索到時平匈,Python拋出NameError異常框沟。

2.4 何時引入作用域

我們知道:

Python中一個名字只有在定義之后,才能引用增炭。

print(i)

直接引用未定義的名字i忍燥,按照搜索規(guī)則,在LGB三個作用域均沒有搜索到名字i(LB相同命名空間)隙姿。拋出NameError異常:

Traceback (most recent call last):
  File "scope_test.py", line 15, in <module>
    print(i)
NameError: name 'i' is not defined

那對于這段代碼呢灾前?

def try_to_define_name():
    '''函數(shù)中定義了名字i,并綁定了一個整數(shù)對象1'''
    i = 1

try_to_define_name()
print(i) #引用名字i之前孟辑,調(diào)用了函數(shù)

在引用名字i之前,明明調(diào)用了函數(shù)蔫敲,定義了名字i饲嗽,可是還是找不到這個名字:

Traceback (most recent call last):
  File "scope_test.py", line 20, in <module>
    print(i) #引用名字i之前,調(diào)用了函數(shù)
NameError: name 'i' is not defined

雖然定義了名字i奈嘿,但是定義在了函數(shù)的局部作用域?qū)?yīng)的局部命名空間中貌虾,按照LEGB搜索規(guī)則,在全局作用域中自然訪問不到局部作用域裙犹;再者尽狠,函數(shù)調(diào)用結(jié)束后衔憨,這個命名空間被銷毀了。

引用名字總是與作用域相關(guān)的袄膏,因此:

Python中一個名字只有在定義之后践图,才能在合適的作用域引用。

那么沉馆,在定義名字時码党,就要注意名字定義的作用域了,以免定義后需要訪問時卻找不到斥黑。所以揖盘,了解Python在何時會引入新的作用域很有必要。一般來說锌奴,B,G兩個作用域的引入在不能夠通過代碼操作的兽狭,能夠通過語句引入的作用域只有E,L了。Python中引入新作用域的語句很有限鹿蜀,總的來說只有兩類一個:

  • 函數(shù)定義引入local作用域或者Enclosing作用域箕慧;本質(zhì)上,lambda和生成器表達(dá)式也是函數(shù)耻姥,會引入新作用域销钝。
  • 類定義引入local作用域;
  • 列表推導(dǎo)式引入local作用域琐簇,傳說在python2中列表推導(dǎo)式不引入新的作用域

幾個會讓有其他高級語言經(jīng)驗的猿困惑的地方:

if語句

if True:
    i = 1
print(i) # output: 1蒸健,而不是NameError

if語句并不會引入新的作用域,所以名字綁定語句i = 1print(i)是在同一個作用域中婉商。

for語句

for i in range(6):
    pass
print(i) #output: 5似忧,而不是NameError

for語句同樣不會引入新的作用域,所以名字i的綁定和重綁定與print(i)在同一個作用域丈秩。這一點Python就比較坑了盯捌,因此寫代碼時切忌for循環(huán)名字要與其他名字不重名才行。

import語句

def import_sys():
    '''import sys module'''
    import sys

import_sys()
print(sys.path) # NameError: name 'sys' is not defined

這個算非正常程序員的寫法了蘑秽,在另一篇文章《Python進(jìn)階 - 對象饺著,名字以及綁定》中介紹過,import語句在函數(shù)import_sys中將名字sys和對應(yīng)模塊綁定肠牲,那sys這個名字還是定義在局部作用域幼衰,跟上面的例子沒有任務(wù)區(qū)別。要時刻切記Python的名字缀雳,對象渡嚣,這個其他編程語言不一樣,但是:

打破第一編程語言認(rèn)知的第二門編程語言,才是值得去學(xué)的好語言识椰。

3. 作用域應(yīng)用

3.1 自由變量可讀不可寫

我不太想用“變量”這個詞形容名字绝葡,奈何變量是家喻戶曉了,Python中的自由變量:

If a variable is used in a code block but not defined there, it is a free variable.

如果引用發(fā)生的代碼塊不是其定義的地方腹鹉,它就是一個自由變量藏畅。專業(yè)一點,就是:

引用名字的作用域中沒有這個名字种蘸,那這個名字就是自由名字

Note: “自由名字”只是作者YY的墓赴,并沒得到廣泛認(rèn)可。

我們已經(jīng)了解了作用域有LEGB的層次航瞭,并按順序搜索名字诫硕。按照搜索順序,當(dāng)?shù)蛯幼饔糜虿淮嬖诖阉髅謺r刊侯,引用高層作用域存在的名字章办,也就是自由名字:
[示例1]

def low_scope():
    print(s)

s = 'upper scope'
low_scope()

很清楚,這段代碼的輸出是upper scope滨彻。
[示例2]

def low_scope():
    s = 'lower scope'

s = 'upper scope'
low_scope()
print(s)

很遺憾藕届,最后的打印語句沒有按照期待打印出lower scope而是打印了upper scope

A special quirk of Python is that – if no global statement is in effect – assignments to names always go into the innermost scope.

Python的一個怪癖是亭饵,如果沒有使用global語句休偶,對名字的賦值語句通常會影響最內(nèi)層作用域。
即賦值語句影響局部作用域辜羊,賦值語句帶來的影響是綁定或重綁定踏兜,但是在當(dāng)前局部作用域的命名空間中,并沒有s這個名字八秃,因此賦值語句在局部作用于定義了同名名字s碱妆,這與外層作用域中的s并不沖突,因為它們分屬不同命名空間昔驱。
這樣疹尾,全局作用域的s沒有被重綁定,結(jié)果就很好解釋了骤肛。

當(dāng)涉及可變對象時纳本,情況又有所不同了:
[示例3]

def low_scope():
    l[0] = 2

l = [1, 2]
low_scope()
print(l) # [2, 2]

很遺憾,最后的打印語句沒有按照期待輸出[1, 2]而是輸出了[2, 2]腋颠。
上一個例子的經(jīng)驗并不能運用在此饮醇,因為list作為一個可變對象,l[0] = 2并不是對名字l的重綁定秕豫,而是對l的第一個元素的重綁定,所以沒有新的名字被定義。因此在函數(shù)中成功更新了全局作用于中l所引用對象的值混移。

注意祠墅,下面的示例跟上面的是不一樣的:
[示例4]

def low_scope():
    l = [2, 2]

l = [1, 2]
low_scope()
print(l) # [1, 2]

我們可以用本節(jié)中示例1的方法解釋它。

綜上歌径,可以認(rèn)為:

自由變量可讀不可寫毁嗦。

3.2 globalnonlocal

總是存在打破規(guī)則的需求:

在低層作用域中需要重綁定高層作用域名字,即通過自由名字重綁定回铛。

于是global語句和nonlocal語句因運而生狗准。

global_stmt ::= "global" identifier ("," identifier)*
The global statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals. It would be impossible to assign to a global variable without global, although free variables may refer to globals without being declared global.

global語句是適用于當(dāng)前代碼塊的聲明語句。列出的標(biāo)識符被解釋為全局名字茵肃。雖然自由名字可以不被聲明為global就能引用全局名字腔长,但是不使用global關(guān)鍵字綁定全局名字是不可能的。

nonlocal_stmt ::= "nonlocal" identifier ("," identifier)*
The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.

nonlocal語句使得列出的名字指向最近封閉函數(shù)中綁定的名字验残,而不是全局名字捞附。默認(rèn)的綁定行為會首先搜索局部作用域。nonlocal語句使得在內(nèi)層函數(shù)中重綁定外層函數(shù)作用域中的名字成為可能您没,即使同名的名字存在于全局作用域鸟召。

經(jīng)典的官方示例:

def scope_test():
    
    def do_local():
        spam = 'local spam'

    def do_nonlocal():
        nonlocal spam # 當(dāng)外層作用域不存在spam名字時,nonlocal不能像global那樣自作主張定義一個
        spam = 'nonlocal spam' # 自由名字spam經(jīng)nonlocal聲明后氨鹏,可以做重綁定操作了欧募,可寫的。

    def do_global():
        global spam # 即使全局作用域中沒有名字spam的定義仆抵,這個語句也能在全局作用域定義名字spam
        spam = 'global spam' # 自有變量spam經(jīng)global聲明后跟继,可以做重綁定操作了,可寫的肢础。

    spam = 'test spam'
    do_local()
    print("After local assignment:", spam) # After local assignment: test spam
    do_nonlocal()
    print("After nonlocal assignment:", spam) # After nonlocal assignment: nonlocal spam
    do_global()
    print("After global assignment:", spam) # After global assignment: nonlocal spam


scope_test()
print("In global scope:", spam) # In global scope: global spam

作者說不行nonlocal的邪:

def nest_outter():
    spam = 'outer'

    def nest_inner():
        nonlocal spam1
        spam1 = 'inner'

    nest_inner()
    print(spam)

nest_outter()

Output:

  File "scope_test.py", line 41
    nonlocal spam1
SyntaxError: no binding for nonlocal 'spam1' found

4. 一些坑

作者曾經(jīng)自信滿滿認(rèn)為透徹了解了Python的作用域还栓,但是一大堆坑踩得觸不及防。

一臉懵逼
4.1 坑1 - UnboundLocalError
def test():
    print(i)
    i = 1

i = 2
test()

Output:

Traceback (most recent call last):
  File "scope_test.py", line 42, in <module>
    test()
  File "scope_test.py", line 38, in test
    print(i)
UnboundLocalError: local variable 'i' referenced before assignment

其實忽略掉全局作用域中i = 2這條語句传轰,都可以理解剩盒。

Usually, the local scope references the local names of the (textually) current function.

Python對局部作用域情有獨鐘,解釋器執(zhí)行到print(i)慨蛙,i在局部作用域沒有辽聊。解釋器嘗試?yán)^續(xù)執(zhí)行后面定義了名字i,解釋器就認(rèn)為代碼在定義之前就是用了名字期贫,所以拋出了這個異常跟匆。如果解釋器解釋完整個函數(shù)都沒有找到名字i,那就會沿著搜索鏈LEGB往上找了,最后找不到拋出NameError異常通砍。

4.2 坑2 - 類的局部作用域
class Test(object):

    i = 1

    def test_print(self):
        print(i)

t = Test()
i = 2
t.test_print()

我就問問大家玛臂,這個輸出什么烤蜕?
當(dāng)然會出乎意料輸出2了,特別是有其他語言經(jīng)驗的人會更加困惑迹冤。

上文強調(diào)過:
函數(shù)命名空間的生命周期是什么讽营? 調(diào)用開始,返回或者異常結(jié)束泡徙,雖然示例中是調(diào)用的方法橱鹏,但其本質(zhì)是調(diào)用類的函數(shù)。
類命名空間的作用域是什么堪藐?類定義開始莉兰,類完成定義結(jié)束。

類定義開始時礁竞,創(chuàng)建新的屬于類的命名空間糖荒,用作局部作用域。類定義完后苏章,命名空間銷毀寂嘉,沒有直接方法訪問到類中的i了(除非通過間接訪問的方式:Test.i)。

方法調(diào)用的本質(zhì)是函數(shù)調(diào)用:

class Test(object):

    i = 1

    def test_print(self):
        print(i)

t = Test()
i = 2
# t.test_print()
Test.test_print(t) # 方法調(diào)用最后轉(zhuǎn)換成函數(shù)調(diào)用的方式

函數(shù)調(diào)用開始枫绅,其作用域與全局作用域有了上下級關(guān)系(LG)泉孩,函數(shù)中i作為自由名字,最后輸出2并淋。
因此寓搬,不能被類中數(shù)據(jù)成員和函數(shù)成員的位置迷惑,始終切記县耽,Python中兩種訪問引用的方式:

  • 直接引用:試圖直接寫名字name引用名字句喷,Python按照搜索LEGB作用域的方式搜索名字。

  • 間接引用:使用objname.attrname的方式引用名字attrname兔毙,Python不搜索作用域唾琼,直接去對象里找屬性。

4.3 坑3 - 列表推導(dǎo)式的局部作用域

一個正常列表推導(dǎo)式:

a = 1
b = [a + i for i in range(10)]
print(b) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

現(xiàn)在把列表推導(dǎo)式放到類中:

class Test(object):

    a = 1
    b = [a + i for i in range(10)]
    print(b)

    def test(self):
        pass

Output:

Traceback (most recent call last):
  File "scope_test.py", line 15, in <module>
    class Test(object):
  File "scope_test.py", line 18, in Test
    b = [a + i for i in range(10)]
  File "scope_test.py", line 18, in <listcomp>
    b = [a + i for i in range(10)]
NameError: name 'a' is not defined

輸出反饋名字a未定義澎剥。

上文強調(diào)過锡溯,解釋器讀取類定義開始class ClassName后,創(chuàng)建命名空間用作局部作用域哑姚。
語句a = 1祭饭,在這個局部作用域中定義了名字i
語句b = [a + i for i in rage(10)],列表推導(dǎo)式同樣創(chuàng)建了一個局部作用域叙量。這個作用域與類定義的局部作用域并沒有上下級關(guān)系倡蝙,所以,自然沒有任何直接訪問名字a的方法绞佩。

Python中只有四種作用域:LEGB寺鸥,因為類定義的局部作用域與列表推導(dǎo)式的局部作用域于不是嵌套函數(shù)關(guān)系猪钮,所以并不能構(gòu)成Enclosing作用域關(guān)系。因此它們是兩個獨立的局部作用域胆建,不能相互訪問躬贡。

既然是兩個獨立局部作用域,那么上述例子就等同于:

def test1():
    i = 1

def test2():
    print(i)

test1()
test2()

期待在test2中訪問test1的名字i眼坏,顯然是不可行的。

參考

  1. Python Scopes and Namespaces
  2. Python命名空間和作用域窺探
  3. Python的作用域
  4. Naming and binding
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酸些,一起剝皮案震驚了整個濱河市宰译,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌魄懂,老刑警劉巖沿侈,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異市栗,居然都是意外死亡缀拭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門填帽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛛淋,“玉大人,你說我怎么就攤上這事篡腌『趾桑” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵嘹悼,是天一觀的道長叛甫。 經(jīng)常有香客問我,道長杨伙,這世上最難降的妖魔是什么其监? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮限匣,結(jié)果婚禮上抖苦,老公的妹妹穿的比我還像新娘。我一直安慰自己膛腐,他們只是感情好睛约,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哲身,像睡著了一般辩涝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勘天,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天怔揩,我揣著相機與錄音捉邢,去河邊找鬼。 笑死商膊,一個胖子當(dāng)著我的面吹牛伏伐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晕拆,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼藐翎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了实幕?” 一聲冷哼從身側(cè)響起吝镣,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昆庇,沒想到半個月后末贾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡整吆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年拱撵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片表蝙。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拴测,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出勇哗,到底是詐尸還是另有隱情昼扛,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布欲诺,位于F島的核電站抄谐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扰法。R本人自食惡果不足惜蛹含,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望塞颁。 院中可真熱鬧浦箱,春花似錦、人聲如沸祠锣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伴网。三九已至蓬推,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間澡腾,已是汗流浹背沸伏。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工糕珊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毅糟。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓红选,卻偏偏與公主長得像,于是被迫代替她去往敵國和親姆另。 傳聞我的和親對象是個殘疾皇子喇肋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容