Python學(xué)習(xí)筆記二十三(閉包 / 裝飾器 )

函數(shù)

什么是函數(shù)? 將具有某種功能的代碼放到一起, 構(gòu)成一個函數(shù).
為什么說函數(shù)? 因為需要研究一個問題, 函數(shù)可以嵌套調(diào)用, 那么可不可以嵌套定義?

函數(shù)的嵌套調(diào)用
def func1():
    print("func1")


def func2():
    # 嵌套調(diào)用
    func1()


func1()
func2()

# 運行結(jié)果
# func1
# func1

函數(shù)能使用函數(shù)名調(diào)用, 那么函數(shù)名是什么? 標(biāo)識符, 標(biāo)識符里包含變量 / 函數(shù)名, 那么函數(shù)能不能通過變量傳遞 / 調(diào)用嗎?

def func1():
    print("func1")


def func2():
    # 函數(shù)通過變量傳遞
    f2 = func1    
    print("f2 type:%s" % type(f2))
    # 通過變量調(diào)用函數(shù)
    f2()


func1()
func2()

# 運行結(jié)果
# func1
# f2 type:<class 'function'>
# func1

函數(shù)可以通過變量傳遞 / 調(diào)用,那么回到最初的問題, 函數(shù)能嵌套調(diào)用, 函數(shù)能嵌套定義嗎?

閉包
def func2():
    # 定義函數(shù)func1
    def func1():
        print("func1")


func2()

# 運行結(jié)果 

通過運行結(jié)果可以得出, 可以嵌套定義 ( 沒報錯:) ) , 那么為什么沒有結(jié)果?
函數(shù)能夠傳遞,函數(shù)在定義的時候不會執(zhí)行, 那么可以明確一點func2 被調(diào)用,所以func2 被執(zhí)行了.
func2 被執(zhí)行 ,那么func1 定義在func2 函數(shù)體內(nèi), 所以func1 被定義了, 但是func1 沒有被調(diào)用.那么能不能通過函數(shù)傳遞調(diào)用func1?

def func2():
    # 定義函數(shù)func1
    def func1():
        print("func1")

    return func1


f = func2()  # 得到func1 的引用 
print(type(f))  # 查看f 的類型
f()  # 調(diào)用函數(shù)

# 運行結(jié)果
# <class 'function'>
# func1

這種形式叫閉包[1], 在函數(shù)內(nèi)部定義另一個函數(shù), 并且外部函數(shù)func2 返回內(nèi)部函數(shù)func1 的引用.
雖然這是一個閉包, 但是沒人會這么用, 用兩個函數(shù)去做一個函數(shù)可以完成的事, 而且還這么麻煩.
相信大家都畫過數(shù)學(xué)中的函數(shù)圖像, 畫圖像需要確定坐標(biāo), 今天就用程序確定 x = 某值時 , y的值 ( y= x的平方 )

def func2(x):
    # 內(nèi)部函數(shù)func1 要使用一個X 的值, 需要從外部傳入
    # 為什么要從外部傳入?
    def func1():
        print(x ** 2)

    return func1


f = func2(2)  # 得到func1 的引用
f()  # 調(diào)用函數(shù)

# 運行結(jié)果
# 4

一定要從外部傳入嗎?不一定,就上面這個例子只是簡單演示閉包的用法, 而且上面的例子用一個函數(shù)也可以完成, 那么閉包大多數(shù)的應(yīng)該用在哪? 裝飾器或者叫語法糖.

函數(shù)裝飾器

只要你寫過類方法 / 靜態(tài)方法, 那么你就用過裝飾器.下面來看看裝飾器

print("start")  # 開始Debug


# 函數(shù)定義 會創(chuàng)建 不會執(zhí)行
def func2(x):
    print("func2 被調(diào)用")
    print("x type = %s" % type(x))

    def func1():
        print("func1 被調(diào)用")
        x()

    return func1


@func2  # 程序暫停,執(zhí)行 func3 = func2(func3)
def func3():
    print("func3 被調(diào)用")


func3()  # 調(diào)用函數(shù)

# 運行結(jié)果
# start
# func2 被調(diào)用
# x type = <class 'function'>
# func1 被調(diào)用
# func3 被調(diào)用


裝飾器的運行 debug
01裝飾器-debug.gif
裝飾器運行 圖解
02裝飾器-圖解.png
裝飾器運行 文字描述
  • 程序執(zhí)行到 line:5, func2 被定義 ,
  • 程序執(zhí)行到 line:16是一個裝飾器,不執(zhí)行,
    • 運行(檢測) line:17 定義func3, 同時檢測到是一個函數(shù),返回 line:16 執(zhí)行func2
    • 如果line:17 不是函數(shù),繼續(xù)向下執(zhí)行檢測,直到遇到函數(shù), 再次返回
  • func2 被執(zhí)行 ( 因為@func2 ==> func3 = func2(func3) ),
    • x = 原func3 ,既 x 指向原func3 的函數(shù)體;
    • print("func2 被調(diào)用") # func2 被調(diào)用
      print("x type = %s" % type(x)) # x type = <class 'function'>
    • func1 被定義
    • return func1, 既 func3 = func2(func3) = func1,裝飾完成func3 被指向func1
  • 程序執(zhí)行到 line:21,因為func3 在上面被裝飾過,所以現(xiàn)在的func3( 裝飾完的 ) 指向func1
    • func3() ==> func1(), 既 line:10 被執(zhí)行, print("func1 被調(diào)用")
    • line:11 因為x = 原func3 ,所以 x() 原func3 的方法體被執(zhí)行, print("func3 被調(diào)用")
    • 函數(shù)調(diào)用完成, 返回被調(diào)用處 ,所以 print("func3 被調(diào)用") ( line:18) 完成后 返回 x() 處(line:11) ,
    • x() 執(zhí)行完(line:11) , 返回func3() 處 (line:21), 程序執(zhí)行完成
特殊記憶方法

根據(jù)上面的裝飾器運行過程, 我們嘗試推出一個方法, 便于記憶和解決裝飾器的過程.

03特殊記憶方法.png
使用特殊記憶方法
print("start")  # 開始Debug


# 函數(shù)定義 不會執(zhí)行
def func2(x):
    print("func2 被調(diào)用")
    print("x type = %s" % type(x))

    def func1():
        print("func1 被調(diào)用")
        x()

    return func1

def func4(x):
    print("func4 被調(diào)用")
    print("x type = %s" % type(x))

    def func5():
        print("func5 被調(diào)用")
        x()

    return func5
@func4
@func2
def func3():
    print("func3 被調(diào)用")


func3()  # 調(diào)用函數(shù)

# 運行結(jié)果
# start
# func2 被調(diào)用
# x type = <class 'function'>
# func4 被調(diào)用
# x type = <class 'function'>
# func5 被調(diào)用
# func1 被調(diào)用
# func3 被調(diào)用

04特殊記憶方法-使用.png

裝飾過程:

  • func3 是一個函數(shù)的定義壓棧
  • @func2 得到func3 函數(shù)的引用 壓棧
  • func2 被調(diào)用 func1 壓棧 ( func2 被調(diào)用 x type = <class 'function'>)
  • @func4 的到func1 函數(shù)的引用 壓棧
  • func4 被調(diào)用 func5 壓棧 ( func4 被調(diào)用 x type = <class 'function'>)

調(diào)用過程:

  • 裝飾后調(diào)用func3 ,func3 指向func5 ,func5被調(diào)用
    • func5 出棧, func5 被調(diào)用
    • x(),x 指向func1 func1 被調(diào)用
  • func1 被調(diào)用 func4 出棧 ( 裝飾過程,執(zhí)行過,出棧無效果, 只有func4出棧, func1 在棧頂次可以調(diào)用) ,func1 被調(diào)用
    • func1 出棧, func1 被調(diào)用
    • x(),x 指向func3 func3 被調(diào)用

通過上面的我們大概了解裝飾過程,以及裝飾后的調(diào)用過程, 那么如果是有參有返回值的函數(shù)怎么裝飾呢?

首先我們知道了 閉包外部函數(shù)的形參存儲著原函數(shù)( 被裝飾函數(shù)) , 閉包內(nèi)部調(diào)用了原函數(shù)( 被裝飾函數(shù)),因為裝飾器的調(diào)用是由解釋器完成的,既@閉包外部函數(shù) , 外部函數(shù)的參數(shù)個數(shù)我們無法改變, 那么只能改變閉包內(nèi)部函數(shù)給 原函數(shù)( 被裝飾函數(shù))傳參.

05傳參.png

如圖, func3 需要有參數(shù),@func2 的參數(shù)個數(shù)無法改變, 我們嘗試改變func1 的參數(shù),同時x 最終指向原函數(shù),所以x 必須和func3 的參數(shù)一致.

print("start")  # 開始Debug


# 函數(shù)定義 不會執(zhí)行
def func2(x):
    def func1(*args, **kwargs):
        print("func1 被調(diào)用")
        print("args %s " % args.__str__())
        print("kwargs %s " % kwargs.__str__())
        x(*args, **kwargs)

    return func1


@func2
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    print("args %s " % args.__str__())
    print("kwargs %s " % kwargs.__str__())


func3("Dragon", "Fang", Dragon=18, Fang="nan")  # 調(diào)用函數(shù)

# 運行結(jié)果
# start
# func1 被調(diào)用
# args ('Dragon', 'Fang') 
# kwargs {'Dragon': 18, 'Fang': 'nan'} 
# func3 被調(diào)用
# args ('Dragon', 'Fang') 
# kwargs {'Dragon': 18, 'Fang': 'nan'} 

通過改變閉包內(nèi)部函數(shù)可以給帶參被裝飾函數(shù)傳參, 那么返回值怎么辦?

  • 調(diào)用函數(shù)都會回到被調(diào)用處,裝飾后func3() 調(diào)用,既func1 被調(diào)用
  • func1 執(zhí)行, x() 被調(diào)用==>原func3 被調(diào)用, 原func3執(zhí)行完成有返回值,返回到 x() 處,
  • x() 得到值, 如果不將值返回, 那么func1 執(zhí)行完默認(rèn)返回None,既 裝飾后func3() 處的到None 值,所以 x() 處需要將得到的值返回.
print("start")  # 開始Debug


# 函數(shù)定義 不會執(zhí)行
def func2(x):
    def func1(*args, **kwargs):
        print("func1 被調(diào)用")
        return x(*args, **kwargs)

    return func1


@func2
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    return "DragonFang"


print(func3("Dragon", "Fang", Dragon=18, Fang="nan"))

# 運行結(jié)果
# start
# func1 被調(diào)用
# func3 被調(diào)用
# DragonFang

上面的裝飾器可以裝飾以下四種函數(shù)

  • 無參數(shù),無返回值
  • 無參數(shù),有返回值
  • 有參數(shù),無返回值
  • 有參數(shù),有返回值
裝飾器應(yīng)用場景

裝飾器可以在不改變原函數(shù)的基礎(chǔ)上,添加新的功能 ( 符合開閉原則[2]), 例如計算函數(shù)運行耗時, 在優(yōu)化時常用.

import time


def func(func_args):
    def in_func(*args, **kwargs):
        # 在函數(shù)開始運行前,得到當(dāng)前時間
        start_time = time.time()

        result_value = func_args(*args, **kwargs)

        # 在函數(shù)運行結(jié)束后,獲取當(dāng)前時間,作差 ==> 得到函數(shù)的運行時間
        print(time.time() - start_time)
        return result_value

    return in_func

@func
def test():
    time.sleep(2)

test()

# 運行結(jié)果
# 2.00081205368042
函數(shù)裝飾器-給裝飾器傳參

有個問題,函數(shù)內(nèi)部嵌套函數(shù)的定義是兩層, 如果是三層呢?或者更多層呢?先看看三層,三層往上不討論,層級太深不是一件好事.

def out_test():
    def test(x):
        def in_test(*args, **kwargs):
            print("func1 被調(diào)用")
            return x(*args, **kwargs)

        return in_test

    return test 

如上, 三層函數(shù), 首先這是一個函數(shù), 可用通過out_test() 得到test(x) 函數(shù)的引用, 然后可以通過test(x) 的到in_test (*args, **kwargs)函數(shù)的引用.
那么如果使用這么一個函數(shù)作為裝飾器,會是什么效果?

06使用三層函數(shù)作為裝飾器.png

報錯?缺少參數(shù)?添加參數(shù),添加不定長參數(shù),一步到位 :)

print("start")  # 開始Debug

 
def out_test(*args, **kwargs):
    print("out_test 被調(diào)用")

    def test(x):
        print("test 被調(diào)用")

        def in_test(*args, **kwargs):
            print("in_test 被調(diào)用")
            return x(*args, **kwargs)

        return in_test

    return test


@out_test("Dragon", "Fang", Dragon=18, Fang="nan")
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    return "DragonFang"


print(func3("Dragon", "Fang", Dragon=18, Fang="nan"))

# 運行結(jié)果
# start
# out_test 被調(diào)用
# test 被調(diào)用
# in_test 被調(diào)用
# func3 被調(diào)用
# DragonFang

最外層被調(diào)用,然后內(nèi)層被調(diào)用, out_test 被調(diào)用, 然后 test 被調(diào)用, 最后 in_test 被調(diào)用, 那么他們分別作了什么事?

def out_test(*args, **kwargs):
    print("out_test 被調(diào)用")
    print(args)
    print(kwargs)

    def test(x):
        print("test 被調(diào)用")

        print(x)

        def in_test(*args, **kwargs):
            print("in_test 被調(diào)用")
            print(args)
            print(kwargs)
            return x(*args, **kwargs)

        return in_test

    return test


@out_test("test", Dragon=20)
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    return "DragonFang"


print(func3("Dragon", "Fang", Dragon=18, Fang="nan"))

# 運行結(jié)果 
# out_test 被調(diào)用
# ('test',)
# {'Dragon': 20}
# test 被調(diào)用
# <function func3 at 0x000001BAA0C48A60>
# in_test 被調(diào)用
# ('Dragon', 'Fang')
# {'Dragon': 18, 'Fang': 'nan'}
# func3 被調(diào)用
# DragonFang

out_test 被調(diào)用打印了值, 然后test 被調(diào)用 , func3 被打印, 因此我們可以推測出@out_test("test", Dragon=20) ==> out_test("test", Dragon=20) 然后 使用 out_test 的返回值對func3 進行裝飾

那么可以通過三層函數(shù)的形式給裝飾器傳參.

類裝飾器

@classmethod / @staticmethod 都是類裝飾器

# classmethod 源碼
class classmethod(object):
    """
    classmethod(function) -> method
    
    Convert a function to be a class method.
    
    A class method receives the class as implicit first argument,
    just like an instance method receives the instance.
    To declare a class method, use this idiom:
    
      class C:
          @classmethod
          def f(cls, arg1, arg2, ...):
              ...
    
    It can be called either on the class (e.g. C.f()) or on an instance
    (e.g. C().f()).  The instance is ignored except for its class.
    If a class method is called for a derived class, the derived class
    object is passed as the implied first argument.
    
    Class methods are different than C++ or Java static methods.
    If you want those, see the staticmethod builtin.
    """
    def __get__(self, *args, **kwargs): # real signature unknown
        """ Return an attribute of instance, which is of type owner. """
        pass

    def __init__(self, function): # real signature unknown; restored from __doc__
        pass

    @staticmethod # known case of __new__
    def __new__(*args, **kwargs): # real signature unknown
        """ Create and return a new object.  See help(type) for accurate signature. """
        pass

    __func__ = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default

    __isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default


    __dict__ = None # (!) real value is ''

仿照@classmethod ,制作一個類裝飾器.

07類裝飾器-必須有init魔法方法.png

報錯?init魔法方法缺少參數(shù)?給參數(shù)

class ClassMethod(object):
    def __init__(self, *args, **kwargs):
        print(args)
        print(kwargs)


@ClassMethod
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    return "DragonFang"

# 運行結(jié)果
# (<function func3 at 0x000001CC350C2E18>,)
# {}

@ClassMethod 調(diào)用了init 魔法方法? 那么應(yīng)該有一個ClassMethod 類的對象被創(chuàng)建,
既@ClassMethod ==> ClassMethod() 得到對象, 使用對象裝飾func3.

驗證,調(diào)用裝飾后的func3

class ClassMethod(object):
    def __init__(self, *args, **kwargs):
        print(args)
        print(kwargs)


@ClassMethod
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    return "DragonFang"

func3()
# 運行結(jié)果
# Traceback (most recent call last):
#   File "E:/workspace/pycharm/pycharm/model.py", line 12, in <module>
#     func3()
# (<function func3 at 0x00000276789F2E18>,)
# TypeError: 'ClassMethod' object is not callable
# {}

報錯? 'ClassMethod' object is not callable ,對象不可調(diào)用, 證明有一對象被創(chuàng)建, 而且對象需要有一個回調(diào)方法. 魔法方法__call__ [3] 可以使一個實例對象變?yōu)橐粋€可調(diào)用對象.

class ClassMethod(object):
    def __init__(self, *args, **kwargs):
        print("__init__")
        print(args)
        print(kwargs)

    def __call__(self, *args, **kwargs):
        print("__call__")
        print(args)
        print(kwargs)

@ClassMethod
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    return "DragonFang"

func3()
# 運行結(jié)果
# __init__
# (<function func3 at 0x00000204124F2E18>,)
# {}
# __call__
# ()
# {}

成功運行,證明我們的類裝飾器沒問題, 那么函數(shù)裝飾器能傳參, 類裝飾器怎么傳參? call 魔法方法有兩個空值, 是不是可以利用一下?

class ClassMethod(object):
    def __init__(self, *args, **kwargs):
        print("__init__")
        print(args)
        print(kwargs)

    def __call__(self, *args, **kwargs):
        print("__call__")
        print(args)
        print(kwargs)


@ClassMethod("Dragon", "Fang", Dragon=18, Fang="nan")
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    return "DragonFang"


func3()

# 運行結(jié)果
# Traceback (most recent call last):
# __init__
#   File "E:/workspace/pycharm/pycharm/model.py", line 19, in <module>
# ('Dragon', 'Fang')
#     func3()
# {'Dragon': 18, 'Fang': 'nan'}
# TypeError: 'NoneType' object is not callable
# __call__
# (<function func3 at 0x000001FCCACB2E18>,)
# {}

call魔法方法被執(zhí)行, 輸出 (<function func3 at 0x000001FCCACB2E18>,) 和 {} ,然后
'NoneType' object is not callable ,空對象不能調(diào)用? 我們知道函數(shù) 或者方法 默認(rèn)是有默認(rèn)返回值的, 默認(rèn)值是None ,也就是說 call 魔法方法返回了一個None 給調(diào)用處,既@ClassMethod("Dragon", "Fang", Dragon=18, Fang="nan") 處, 使用 @None 裝飾func3 是不對的, 那么我們返回一個函數(shù), 既使用函數(shù)裝飾器裝飾func3

class ClassMethod(object):
    def __init__(self, *args, **kwargs):
        print("__init__")
        self.args = args
        self.kwargs = kwargs

    def __call__(self, func):
        print("__call__")
        self.func = func
        return self.test

    def test(self):
        print("test")
        self.func(*self.args, **self.kwargs)


@ClassMethod("Dragon", "Fang", Dragon=18, Fang="nan")
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    print(args)
    print(kwargs)


func3()

# 運行結(jié)果
# __init__
# __call__
# test
# func3 被調(diào)用
# ('Dragon', 'Fang')
# {'Dragon': 18, 'Fang': 'nan'}

運行成功,給類裝飾器傳參完成,有幾點需要注意

  • 參數(shù),參數(shù)是傳給init 魔法方法的,需要使用屬性接收
  • call 魔法方法返回的是一個函數(shù)的引用
  • test方法中,self.func 是原函數(shù), 通過屬性傳不定長參數(shù)需要拆包

類裝飾器傳參-方式2

上面是在初始化時傳參,那能不能將通過方法傳參?

class ClassMethod(object):
    def __init__(self, *args, **kwargs):
        print("__init__")
        self.args = args
        self.kwargs = kwargs

    def __call__(self, func):
        print("__call__")
        self.func = func
        return self.test
    @classmethod
    def test(cls, *args, **kwargs): 
        cls.args = args
        cls.kwargs = kwargs


@ClassMethod.test("Dragon", "Fang", Dragon=18, Fang="nan")
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    print(args)
    print(kwargs)


func3()

# 運行結(jié)果
# Traceback (most recent call last):
# test
#   File "E:/workspace/pycharm/pycharm/model.py", line 18, in <module>
#     @ClassMethod.test("Dragon", "Fang", Dragon=18, Fang="nan")
# TypeError: 'NoneType' object is not callable

test 被打印,說明test 類方法被調(diào)用, 'NoneType' object is not callable 又是這錯誤! 給test類方法一個返回值

class ClassMethod(object):
    def __init__(self, *args, **kwargs):
        print("__init__")

        print(args)
        print(kwargs)

    def __call__(self):
        print("__call__")

    @classmethod
    def test(cls, *args, **kwargs):
        cls.args = args
        cls.kwargs = kwargs
        return cls


@ClassMethod.test("Dragon", "Fang", Dragon=18, Fang="nan")
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    print(args)
    print(kwargs)


func3()

# 運行結(jié)果
# __init__
# (<function func3 at 0x0000023DD1962E18>,)
# {}
# __call__

通過以上實驗可以推斷@ClassMethod.test("Dragon", "Fang", Dragon=18, Fang="nan")
==> 先調(diào)用test 類方法, 然后 調(diào)用 init魔法方法初始化對象, 最后通過實例對象裝飾func3
那么按照這個順序完善一下ClassMethod 類

class ClassMethod(object):
    def __init__(self, func):
        print("__init__")

        self.func = func

    def __call__(self):
        return self.func(*ClassMethod.args, **ClassMethod.kwargs)

    @classmethod
    def test(cls, *args, **kwargs):
        cls.args = args
        cls.kwargs = kwargs
        return cls


@ClassMethod.test("Dragon", "Fang", Dragon=18, Fang="nan")
def func3(*args, **kwargs):
    print("func3 被調(diào)用")
    print(args)
    print(kwargs)


func3()

# 運行結(jié)果
# __init__
# func3 被調(diào)用
# ('Dragon', 'Fang')
# {'Dragon': 18, 'Fang': 'nan'}

perfect 完美

總結(jié):

  • 閉包:函數(shù)內(nèi)部嵌套函數(shù)的定義,構(gòu)成閉包
  • 閉包的作用:大部分用在裝飾器
  • 裝飾器:@函數(shù)名 / @類名 的形式叫做裝飾器或者語法糖
  • 裝飾器的作用:在不改變原函數(shù)的基礎(chǔ)上添加功能
  • 裝飾前的函數(shù)原函數(shù),被閉包外部函數(shù)的形參保存
  • 裝飾后的函數(shù),實際上是閉包的內(nèi)部函數(shù),
  • 通用裝飾器格式:
# 不需要給裝飾器傳參
def func(func_args):
    def in_func(*args, **kwargs):
        print("添加功能")
        return func_args(*args, **kwargs)

    return in_func
# 需要給裝飾器傳參
def out_func(*out_args, **out_kwargs):
    def func(func_args):
        def in_func(*args, **kwargs):
            print("添加功能")
            return func_args(*args, **kwargs)

        return in_func

    return func
  • 裝飾器特殊記憶方式,出棧入棧,入棧: 裝飾過程,出棧:調(diào)用過程
  • 類裝飾器,類需要包含 init 魔法方法 和 call 魔法方法 構(gòu)成基本的類裝飾器
  • 類裝飾器傳參,除了init 魔法方法 和 call 魔法方法 之外需要定義另外的方法
    • @ClassMethod("Dragon", "Fang", Dragon=18, Fang="nan"), 這種傳參方式需要定義另外的方法, 輔助調(diào)用原函數(shù)
    • @ClassMethod.test("Dragon", "Fang", Dragon=18, Fang="nan"), 這種傳參方式需要定義另外的方式, 輔助保存參數(shù)

到此結(jié)?DragonFangQy 2018.5.26


  1. 閉包 ?

  2. 開閉原則 ?

  3. __call__方法 ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捉撮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子逮刨,更是在濱河造成了極大的恐慌,老刑警劉巖堵泽,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件修己,死亡現(xiàn)場離奇詭異,居然都是意外死亡迎罗,警方通過查閱死者的電腦和手機睬愤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纹安,“玉大人尤辱,你說我怎么就攤上這事砂豌。” “怎么了光督?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵阳距,是天一觀的道長。 經(jīng)常有香客問我结借,道長筐摘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任船老,我火速辦了婚禮咖熟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柳畔。我一直安慰自己馍管,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布薪韩。 她就那樣靜靜地躺著确沸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪躬存。 梳的紋絲不亂的頭發(fā)上张惹,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音岭洲,去河邊找鬼宛逗。 笑死,一個胖子當(dāng)著我的面吹牛盾剩,可吹牛的內(nèi)容都是我干的雷激。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼告私,長吁一口氣:“原來是場噩夢啊……” “哼屎暇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起驻粟,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤根悼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蜀撑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挤巡,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年酷麦,在試婚紗的時候發(fā)現(xiàn)自己被綠了矿卑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡沃饶,死狀恐怖母廷,靈堂內(nèi)的尸體忽然破棺而出轻黑,到底是詐尸還是另有隱情,我是刑警寧澤琴昆,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布氓鄙,位于F島的核電站,受9級特大地震影響椎咧,放射性物質(zhì)發(fā)生泄漏玖详。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一勤讽、第九天 我趴在偏房一處隱蔽的房頂上張望蟋座。 院中可真熱鬧夺荒,春花似錦亲善、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽券膀。三九已至,卻和暖如春驯遇,著一層夾襖步出監(jiān)牢的瞬間芹彬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工叉庐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舒帮,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓陡叠,卻偏偏與公主長得像玩郊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子枉阵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

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

  • 要點: 函數(shù)式編程:注意不是“函數(shù)編程”译红,多了一個“式” 模塊:如何使用模塊 面向?qū)ο缶幊蹋好嫦驅(qū)ο蟮母拍睢傩浴?..
    victorsungo閱讀 1,510評論 0 6
  • 某夜R君加班到九點,想來回到宿舍也是孤身一人拙徽,定然百無聊奈刨沦,加之最近心緒頗不寧靜,把自己窩著也只會更添惆悵斋攀。...
    譚澤榮閱讀 668評論 1 3
  • 打馬虎眼已卷,是最近一次會議上梧田,boss給總結(jié)的詞淳蔼,再次回憶起來當(dāng)時情景侧蘸,仍是羞愧難當(dāng)。自認(rèn)為相當(dāng)負(fù)責(zé)的我鹉梨,卻是無口否...
    真是奇葩了閱讀 1,125評論 0 0
  • 人究竟是怎樣一種生物讳癌? 我走在人海中, 停下了自己的步伐存皂。 一張張面孔晌坤, 迎面走來, 憂愁旦袋,喜悅骤菠,呆滯,銳利疤孕,慈祥...
    海王星1984閱讀 248評論 0 1
  • 薛瑞平老師的書確實特別好看商乎。班級日志,閱讀課祭阀,都是分年級鹉戚,分學(xué)期。有節(jié)奏有序列专控,循序漸進抹凳。無論是對工作,還是生活伦腐,...
    笑笑8閱讀 148評論 0 1