python裝飾器詳解

Python中的裝飾器是你進入Python大門的一道坎厦酬,不管你跨不跨過去它都在那里憾儒。

為什么需要裝飾器

我們假設(shè)你的程序?qū)崿F(xiàn)了say_hello()say_goodbye()兩個函數(shù)中狂。

def say_hello():

? ? print "hello!"


def say_goodbye():

? ? print "hello!"? # bug here

if __name__ == '__main__':

? ? say_hello()

? ? say_goodbye()

但是在實際調(diào)用中,我們發(fā)現(xiàn)程序出錯了扑毡,上面的代碼打印了兩個hello胃榕。經(jīng)過調(diào)試你發(fā)現(xiàn)是say_goodbye()出錯了。老板要求調(diào)用每個方法前都要記錄進入函數(shù)的名稱瞄摊,比如這樣:

[DEBUG]: Enter say_hello()

Hello!

[DEBUG]: Enter say_goodbye()

Goodbye!

好勋又,小A是個畢業(yè)生,他是這樣實現(xiàn)的换帜。

def say_hello():

? ? print "[DEBUG]: enter say_hello()"

? ? print "hello!"

def say_goodbye():

? ? print "[DEBUG]: enter say_goodbye()"

? ? print "hello!"

if __name__ == '__main__':

? ? say_hello()

? ? say_goodbye()

很low吧楔壤? 嗯是的。小B工作有一段時間了惯驼,他告訴小A可以這樣寫蹲嚣。

def debug():

? ? import inspect

? ? caller_name = inspect.stack()[1][3]

? ? print "[DEBUG]: enter {}()".format(caller_name)?

def say_hello():

? ? debug()

? ? print "hello!"

def say_goodbye():

? ? debug()

? ? print "goodbye!"

if __name__ == '__main__':

? ? say_hello()

? ? say_goodbye()

是不是好一點?那當(dāng)然祟牲,但是每個業(yè)務(wù)函數(shù)里都要調(diào)用一下debug()函數(shù)隙畜,是不是很難受?萬一老板說say相關(guān)的函數(shù)不用debug说贝,do相關(guān)的才需要呢议惰?

那么裝飾器這時候應(yīng)該登場了。

裝飾器本質(zhì)上是一個Python函數(shù)乡恕,它可以讓其他函數(shù)在不需要做任何代碼變動的前提下增加額外功能言询,裝飾器的返回值也是一個函數(shù)對象俯萎。它經(jīng)常用于有切面需求的場景,比如:插入日志运杭、性能測試夫啊、事務(wù)處理、緩存辆憔、權(quán)限校驗等場景涮母。裝飾器是解決這類問題的絕佳設(shè)計,有了裝飾器躁愿,我們就可以抽離出大量與函數(shù)功能本身無關(guān)的雷同代碼并繼續(xù)重用。

概括的講沪蓬,裝飾器的作用就是為已經(jīng)存在的函數(shù)或?qū)ο筇砑宇~外的功能彤钟。


怎么寫一個裝飾器

在早些時候 (Python Version < 2.4,2004年以前)跷叉,為一個函數(shù)添加額外功能的寫法是這樣的逸雹。

def debug(func):

? ? def wrapper():

? ? ? ? print "[DEBUG]: enter {}()".format(func.__name__)

? ? ? ? return func()

? ? return wrapper

def say_hello():

? ? print "hello!"

say_hello = debug(say_hello)? # 添加功能并保持原函數(shù)名不變

上面的debug函數(shù)其實已經(jīng)是一個裝飾器了,它對原函數(shù)做了包裝并返回了另外一個函數(shù)云挟,額外添加了一些功能梆砸。因為這樣寫實在不太優(yōu)雅,在后面版本的Python中支持了@語法糖园欣,下面代碼等同于早期的寫法帖世。

def debug(func):

? ? def wrapper():

? ? ? ? print "[DEBUG]: enter {}()".format(func.__name__)

? ? ? ? return func()

? ? return wrapper

@debug

def say_hello():

? ? print "hello!"

這是最簡單的裝飾器,但是有一個問題沸枯,如果被裝飾的函數(shù)需要傳入?yún)?shù)日矫,那么這個裝飾器就壞了。因為返回的函數(shù)并不能接受參數(shù)绑榴,你可以指定裝飾器函數(shù)wrapper接受和原函數(shù)一樣的參數(shù)哪轿,比如:

def debug(func):

? ? def wrapper(something):? # 指定一毛一樣的參數(shù)

? ? ? ? print "[DEBUG]: enter {}()".format(func.__name__)

? ? ? ? return func(something)

? ? return wrapper? # 返回包裝過函數(shù)

@debug

def say(something):

? ? print "hello {}!".format(something)

這樣你就解決了一個問題,但又多了N個問題翔怎。因為函數(shù)有千千萬窃诉,你只管你自己的函數(shù),別人的函數(shù)參數(shù)是什么樣子赤套,鬼知道飘痛?還好Python提供了可變參數(shù)*args和關(guān)鍵字參數(shù)**kwargs,有了這兩個參數(shù)于毙,裝飾器就可以用于任意目標(biāo)函數(shù)了敦冬。

def debug(func):

? ? def wrapper(*args, **kwargs):? # 指定宇宙無敵參數(shù)

? ? ? ? print "[DEBUG]: enter {}()".format(func.__name__)

? ? ? ? print 'Prepare and say...',

? ? ? ? return func(*args, **kwargs)

? ? return wrapper? # 返回

@debug

def say(something):

? ? print "hello {}!".format(something)

至此,你已完全掌握初級的裝飾器寫法唯沮。

高級一點的裝飾器

帶參數(shù)的裝飾器和類裝飾器屬于進階的內(nèi)容脖旱。在理解這些裝飾器之前堪遂,最好對函數(shù)的閉包和裝飾器的接口約定有一定了解。(參見Python的閉包)


帶參數(shù)的裝飾器

假設(shè)我們前文的裝飾器需要完成的功能不僅僅是能在進入某個函數(shù)后打出log信息萌庆,而且還需指定log的級別溶褪,那么裝飾器就會是這樣的。

def logging(level):

? ? def wrapper(func):

? ? ? ? def inner_wrapper(*args, **kwargs):

? ? ? ? ? ? print "[{level}]: enter function {func}()".format(

? ? ? ? ? ? ? ? level=level,

? ? ? ? ? ? ? ? func=func.__name__)

? ? ? ? ? ? return func(*args, **kwargs)

? ? ? ? return inner_wrapper

? ? return wrapper

@logging(level='INFO')

def say(something):

? ? print "say {}!".format(something)

# 如果沒有使用@語法践险,等同于

# say = logging(level='INFO')(say)

@logging(level='DEBUG')

def do(something):

? ? print "do {}...".format(something)

if __name__ == '__main__':

? ? say('hello')

? ? do("my work")

是不是有一些暈猿妈?你可以這么理解,當(dāng)帶參數(shù)的裝飾器被打在某個函數(shù)上時巍虫,比如@logging(level='DEBUG')彭则,它其實是一個函數(shù),會馬上被執(zhí)行占遥,只要這個它返回的結(jié)果是一個裝飾器時俯抖,那就沒問題。細細再體會一下瓦胎。


基于類實現(xiàn)的裝飾器

裝飾器函數(shù)其實是這樣一個接口約束芬萍,它必須接受一個callable對象作為參數(shù),然后返回一個callable對象搔啊。在Python中一般callable對象都是函數(shù)柬祠,但也有例外。只要某個對象重載了__call__()方法负芋,那么這個對象就是callable的漫蛔。

class Test():

? ? def __call__(self):

? ? ? ? print 'call me!'

t = Test()

t()? # call me

__call__這樣前后都帶下劃線的方法在Python中被稱為內(nèi)置方法,有時候也被稱為魔法方法旧蛾。重載這些魔法方法一般會改變對象的內(nèi)部行為惩猫。上面這個例子就讓一個類對象擁有了被調(diào)用的行為。

回到裝飾器上的概念上來蚜点,裝飾器要求接受一個callable對象轧房,并返回一個callable對象(不太嚴謹,詳見后文)绍绘。那么用類來實現(xiàn)也是也可以的奶镶。我們可以讓類的構(gòu)造函數(shù)__init__()接受一個函數(shù),然后重載__call__()并返回一個函數(shù)陪拘,也可以達到裝飾器函數(shù)的效果厂镇。

class logging(object):

? ? def __init__(self, func):

? ? ? ? self.func = func

? ? def __call__(self, *args, **kwargs):

? ? ? ? print "[DEBUG]: enter function {func}()".format(

? ? ? ? ? ? func=self.func.__name__)

? ? ? ? return self.func(*args, **kwargs)

@logging

def say(something):

? ? print "say {}!".format(something)

帶參數(shù)的類裝飾器

如果需要通過類形式實現(xiàn)帶參數(shù)的裝飾器,那么會比前面的例子稍微復(fù)雜一點左刽。那么在構(gòu)造函數(shù)里接受的就不是一個函數(shù)捺信,而是傳入的參數(shù)。通過類把這些參數(shù)保存起來。然后在重載__call__方法是就需要接受一個函數(shù)并返回一個函數(shù)迄靠。

class logging(object):

? ? def __init__(self, level='INFO'):

? ? ? ? self.level = level


? ? def __call__(self, func): # 接受函數(shù)

? ? ? ? def wrapper(*args, **kwargs):

? ? ? ? ? ? print "[{level}]: enter function {func}()".format(

? ? ? ? ? ? ? ? level=self.level,

? ? ? ? ? ? ? ? func=func.__name__)

? ? ? ? ? ? func(*args, **kwargs)

? ? ? ? return wrapper? #返回函數(shù)

@logging(level='INFO')

def say(something):

? ? print "say {}!".format(something)


內(nèi)置的裝飾器

內(nèi)置的裝飾器和普通的裝飾器原理是一樣的秒咨,只不過返回的不是函數(shù),而是類對象掌挚,所以更難理解一些雨席。

@property

在了解這個裝飾器前,你需要知道在不使用裝飾器怎么寫一個屬性吠式。

def getx(self):

? ? return self._x

def setx(self, value):

? ? self._x = value


def delx(self):

? del self._x

# create a property

x = property(getx, setx, delx, "I am doc for x property")

以上就是一個Python屬性的標(biāo)準(zhǔn)寫法陡厘,其實和Java挺像的,但是太羅嗦特占。有了@語法糖糙置,能達到一樣的效果但看起來更簡單。

@property

def x(self): ...

# 等同于

def x(self): ...

x = property(x)

屬性有三個裝飾器:setter, getter, deleter 是目,都是在property()的基礎(chǔ)上做了一些封裝罢低,因為setter和deleter是property()的第二和第三個參數(shù),不能直接套用@語法胖笛。getter裝飾器和不帶getter的屬性裝飾器效果是一樣的,估計只是為了湊數(shù)宜岛,本身沒有任何存在的意義长踊。經(jīng)過@property裝飾過的函數(shù)返回的不再是一個函數(shù),而是一個property對象萍倡。

>>> property()

<property object at 0x10ff07940>

@staticmethod身弊,@classmethod

有了@property裝飾器的了解,這兩個裝飾器的原理是差不多的列敲。@staticmethod返回的是一個staticmethod類對象阱佛,而@classmethod返回的是一個classmethod類對象。他們都是調(diào)用的是各自的__init__()構(gòu)造函數(shù)戴而。

class classmethod(object):

? ? """

? ? classmethod(function) -> method

? ? """? ?

? ? def __init__(self, function): # for @classmethod decorator

? ? ? ? pass

? ? # ...

class staticmethod(object):

? ? """

? ? staticmethod(function) -> method

? ? """

? ? def __init__(self, function): # for @staticmethod decorator

? ? ? ? pass

? ? # ...

裝飾器的@語法就等同調(diào)用了這兩個類的構(gòu)造函數(shù)凑术。

class Foo(object):

? ? @staticmethod

? ? def bar():

? ? ? ? pass


? ? # 等同于 bar = staticmethod(bar)

至此,我們上文提到的裝飾器接口定義可以更加明確一些所意,裝飾器必須接受一個callable對象淮逊,其實它并不關(guān)心你返回什么,可以是另外一個callable對象(大部分情況)扶踊,也可以是其他類對象泄鹏,比如property。


裝飾器里的那些坑

裝飾器可以讓你代碼更加優(yōu)雅秧耗,減少重復(fù)备籽,但也不全是優(yōu)點,也會帶來一些問題分井。

位置錯誤的代碼

讓我們直接看示例代碼车猬。

def html_tags(tag_name):

? ? print 'begin outer function.'

? ? def wrapper_(func):

? ? ? ? print "begin of inner wrapper function."

? ? ? ? def wrapper(*args, **kwargs):

? ? ? ? ? ? content = func(*args, **kwargs)

? ? ? ? ? ? print "<{tag}>{content}</{tag}>".format(tag=tag_name, content=content)

? ? ? ? print 'end of inner wrapper function.'

? ? ? ? return wrapper

? ? print 'end of outer function'

? ? return wrapper_

@html_tags('b')

def hello(name='Toby'):

? ? return 'Hello {}!'.format(name)

hello()

hello()

在裝飾器中我在各個可能的位置都加上了print語句霉猛,用于記錄被調(diào)用的情況。你知道他們最后打印出來的順序嗎诈唬?如果你心里沒底韩脏,那么最好不要在裝飾器函數(shù)之外添加邏輯功能,否則這個裝飾器就不受你控制了铸磅。以下是輸出結(jié)果:

begin outer function.

end of outer function

begin of inner wrapper function.

end of inner wrapper function.

<b>Hello Toby!</b>

<b>Hello Toby!</b>

錯誤的函數(shù)簽名和文檔

裝飾器裝飾過的函數(shù)看上去名字沒變赡矢,其實已經(jīng)變了。

def logging(func):

? ? def wrapper(*args, **kwargs):

? ? ? ? """print log before a function."""

? ? ? ? print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)

? ? ? ? return func(*args, **kwargs)

? ? return wrapper

@logging

def say(something):

? ? """say something"""

? ? print "say {}!".format(something)

print say.__name__? # wrapper

為什么會這樣呢阅仔?只要你想想裝飾器的語法糖@代替的東西就明白了吹散。@等同于這樣的寫法。

say = logging(say)

logging其實返回的函數(shù)名字剛好是wrapper八酒,那么上面的這個語句剛好就是把這個結(jié)果賦值給say空民,say的__name__自然也就是wrapper了,不僅僅是name羞迷,其他屬性也都是來自wrapper界轩,比如doc,source等等衔瓮。

使用標(biāo)準(zhǔn)庫里的functools.wraps浊猾,可以基本解決這個問題。

from functools import wraps

def logging(func):

? ? @wraps(func)

? ? def wrapper(*args, **kwargs):

? ? ? ? """print log before a function."""

? ? ? ? print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)

? ? ? ? return func(*args, **kwargs)

? ? return wrapper

@logging

def say(something):

? ? """say something"""

? ? print "say {}!".format(something)

print say.__name__? # say

print say.__doc__ # say something

看上去不錯热鞍!主要問題解決了葫慎,但其實還不太完美。因為函數(shù)的簽名和源碼還是拿不到的薇宠。

import inspect

print inspect.getargspec(say)? # failed

print inspect.getsource(say)? # failed

如果要徹底解決這個問題可以借用第三方包偷办,比如wrapt。后文有介紹澄港。

不能裝飾@staticmethod 或者 @classmethod

當(dāng)你想把裝飾器用在一個靜態(tài)方法或者類方法時椒涯,不好意思,報錯了回梧。

class Car(object):

? ? def __init__(self, model):

? ? ? ? self.model = model

? ? @logging? # 裝飾實例方法逐工,OK

? ? def run(self):

? ? ? ? print "{} is running!".format(self.model)

? ? @logging? # 裝飾靜態(tài)方法,F(xiàn)ailed

? ? @staticmethod

? ? def check_model_for(obj):

? ? ? ? if isinstance(obj, Car):

? ? ? ? ? ? print "The model of your car is {}".format(obj.model)

? ? ? ? else:

? ? ? ? ? ? print "{} is not a car!".format(obj)

"""

Traceback (most recent call last):

...

? File "example_4.py", line 10, in logging

? ? @wraps(func)

? File "C:\Python27\lib\functools.py", line 33, in update_wrapper

? ? setattr(wrapper, attr, getattr(wrapped, attr))

AttributeError: 'staticmethod' object has no attribute '__module__'

"""

前面已經(jīng)解釋了@staticmethod這個裝飾器漂辐,其實它返回的并不是一個callable對象泪喊,而是一個staticmethod對象,那么它是不符合裝飾器要求的(比如傳入一個callable對象)髓涯,你自然不能在它之上再加別的裝飾器袒啼。要解決這個問題很簡單,只要把你的裝飾器放在@staticmethod之前就好了,因為你的裝飾器返回的還是一個正常的函數(shù)蚓再,然后再加上一個@staticmethod是不會出問題的滑肉。

class Car(object):

? ? def __init__(self, model):

? ? ? ? self.model = model

? ? @staticmethod

? ? @logging? # 在@staticmethod之前裝飾,OK

? ? def check_model_for(obj):

? ? ? ? pass


如何優(yōu)化你的裝飾器

嵌套的裝飾函數(shù)不太直觀摘仅,我們可以使用第三方包類改進這樣的情況靶庙,讓裝飾器函數(shù)可讀性更好。

decorator.py

decorator.py 是一個非常簡單的裝飾器加強包娃属。你可以很直觀的先定義包裝函數(shù)wrapper()六荒,再使用decorate(func, wrapper)方法就可以完成一個裝飾器。

from decorator import decorate

def wrapper(func, *args, **kwargs):

? ? """print log before a function."""

? ? print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)

? ? return func(*args, **kwargs)

def logging(func):

? ? return decorate(func, wrapper)? # 用wrapper裝飾func

你也可以使用它自帶的@decorator裝飾器來完成你的裝飾器矾端。

from decorator import decorator

@decorator

def logging(func, *args, **kwargs):

? ? print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)

? ? return func(*args, **kwargs)

decorator.py實現(xiàn)的裝飾器能完整保留原函數(shù)的name掏击,doc和args,唯一有問題的就是inspect.getsource(func)返回的還是裝飾器的源代碼秩铆,你需要改成inspect.getsource(func.__wrapped__)砚亭。

wrapt

wrapt是一個功能非常完善的包,用于實現(xiàn)各種你想到或者你沒想到的裝飾器殴玛。使用wrapt實現(xiàn)的裝飾器你不需要擔(dān)心之前inspect中遇到的所有問題捅膘,因為它都幫你處理了,甚至inspect.getsource(func)也準(zhǔn)確無誤滚粟。

import wrapt

# without argument in decorator

@wrapt.decorator

def logging(wrapped, instance, args, kwargs):? # instance is must

? ? print "[DEBUG]: enter {}()".format(wrapped.__name__)

? ? return wrapped(*args, **kwargs)

@logging

def say(something): pass

使用wrapt你只需要定義一個裝飾器函數(shù)寻仗,但是函數(shù)簽名是固定的,必須是(wrapped, instance, args, kwargs)坦刀,注意第二個參數(shù)instance是必須的,就算你不用它蔬咬。當(dāng)裝飾器裝飾在不同位置時它將得到不同的值鲤遥,比如裝飾在類實例方法時你可以拿到這個類實例。根據(jù)instance的值你能夠更加靈活的調(diào)整你的裝飾器林艘。另外盖奈,args和kwargs也是固定的,注意前面沒有星號狐援。在裝飾器內(nèi)部調(diào)用原函數(shù)時才帶星號钢坦。

如果你需要使用wrapt寫一個帶參數(shù)的裝飾器,可以這樣寫啥酱。

def logging(level):

? ? @wrapt.decorator

? ? def wrapper(wrapped, instance, args, kwargs):

? ? ? ? print "[{}]: enter {}()".format(level, wrapped.__name__)

? ? ? ? return wrapped(*args, **kwargs)

? ? return wrapper

@logging(level="INFO")

def do(work): pass

小結(jié)

Python的裝飾器和Java的注解(Annotation)并不是同一回事爹凹,和C#中的特性(Attribute)也不一樣,完全是兩個概念镶殷。

裝飾器的理念是對原函數(shù)禾酱、對象的加強,相當(dāng)于重新封裝,所以一般裝飾器函數(shù)都被命名為wrapper()颤陶,意義在于包裝颗管。函數(shù)只有在被調(diào)用時才會發(fā)揮其作用。比如@logging裝飾器可以在函數(shù)執(zhí)行時額外輸出日志滓走,@cache裝飾過的函數(shù)可以緩存計算結(jié)果等等垦江。

而注解和特性則是對目標(biāo)函數(shù)或?qū)ο筇砑右恍傩裕喈?dāng)于將其分類搅方。這些屬性可以通過反射拿到比吭,在程序運行時對不同的特性函數(shù)或?qū)ο蠹右愿深A(yù)。比如帶有Setup的函數(shù)就當(dāng)成準(zhǔn)備步驟執(zhí)行腰懂,或者找到所有帶有TestMethod的函數(shù)依次執(zhí)行等等梗逮。

至此我所了解的裝飾器已經(jīng)講完,但是還有一些內(nèi)容沒有提到绣溜,比如裝飾類的裝飾器慷彤。有機會再補充。謝謝觀看怖喻。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末底哗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子锚沸,更是在濱河造成了極大的恐慌跋选,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哗蜈,死亡現(xiàn)場離奇詭異前标,居然都是意外死亡,警方通過查閱死者的電腦和手機距潘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門炼列,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人音比,你說我怎么就攤上這事俭尖。” “怎么了洞翩?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵稽犁,是天一觀的道長。 經(jīng)常有香客問我骚亿,道長已亥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任来屠,我火速辦了婚禮陷猫,結(jié)果婚禮上秫舌,老公的妹妹穿的比我還像新娘。我一直安慰自己绣檬,他們只是感情好足陨,可當(dāng)我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著娇未,像睡著了一般墨缘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上零抬,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天镊讼,我揣著相機與錄音,去河邊找鬼平夜。 笑死蝶棋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的忽妒。 我是一名探鬼主播玩裙,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼段直!你這毒婦竟也來了吃溅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤鸯檬,失蹤者是張志新(化名)和其女友劉穎决侈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喧务,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡赖歌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了功茴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庐冯。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖痊土,靈堂內(nèi)的尸體忽然破棺而出肄扎,到底是詐尸還是另有隱情墨林,我是刑警寧澤赁酝,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站旭等,受9級特大地震影響酌呆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搔耕,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一隙袁、第九天 我趴在偏房一處隱蔽的房頂上張望痰娱。 院中可真熱鬧,春花似錦菩收、人聲如沸梨睁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坡贺。三九已至,卻和暖如春箱舞,著一層夾襖步出監(jiān)牢的瞬間遍坟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工晴股, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留愿伴,地道東北人。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓电湘,卻偏偏與公主長得像隔节,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子胡桨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,566評論 2 349

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