python metaclass 詳細說明

<meta charset="utf-8">

Class也是Object

在理解metaclass之前屈溉,我們需要先理解Python中的class们镜。從某種程度上來說缴渊,Python中的class的定位比較特殊仑乌。

對于大部分面向對象語言來說兑燥,class是一段定義了如何產(chǎn)生object的代碼塊亮瓷。在Python中這一定義也成立:

>>> class example(object):
...     pass
...
>>> object1 = example()
>>> print(object1)
<__main__.example object at 0x102e26990>

但是在Python中降瞳,class并不只有這一角色嘱支。class實際上也是object。當我們使用class定義一個類的時候,Python會執(zhí)行相應代碼并在內(nèi)存中創(chuàng)建一個名為example的object除师。

但該object(class)是具有創(chuàng)建其他object(instance)的能力的赢织。這也是這個object是一個class的原因消返。由于本質上class任然是一個object狠毯,所以我們可以對class做出以下操作:

  • 我們可以將其賦給一個變量
  • 我們可以對其進行拷貝
  • 我們可以賦給其新的變量
  • 我們可以將其作為參數(shù)賦給其他的函數(shù)

舉例如下:

# print a class since it's an object
>>> print(example)
<class '__main__.example'>

# assign an attribute to the class
>>> print(hasattr(example, 'new_attribute'))
False
>>> example.new_attribute = 'assign an attribute to the class'
>>> print(hasattr(example, 'new_attribute'))
True
>>> print(example.new_attribute)
assign an attribute to the class

# assign the class to a variable
>>> example_mirror = example
>>> print(example_mirror)
<class '__main__.example'>
>>> print(example_mirror())
<__main__.example object at 0x102e26a90>

# pass class as a parameter
>>> def echo(cls):
...     print(cls)
...
>>> echo(example)
<class '__main__.example'>

動態(tài)創(chuàng)建class

既然class也是object,那么我們就可以像創(chuàng)建普通的object一樣動態(tài)創(chuàng)建class亭引。

第一種方法贞岭,我們可以在方法中創(chuàng)建class八毯。如下面的例子所示:

>>> def dynamic_class_creater(name):
...     if name == 'name1':
...         class class1(object):
...             pass
...         return class1
...     else:
...         class class2(object):
...             pass
...         return class2
...
>>> first_class = dynamic_class_creater('name1')
>>> print(first_class)
<class '__main__.class1'>
>>> print(first_class())
<__main__.class1 object at 0x10e4149d0>

但通過這種方式創(chuàng)建class并沒有特別動態(tài)。我們?nèi)稳恍枰约憾x類的具體內(nèi)容瞄桨』八伲考慮到class也是object,那么也一定有某種方法能夠像產(chǎn)生instance一樣產(chǎn)生類芯侥。

當我們使用class關鍵字創(chuàng)建類的時候泊交,Python會自動創(chuàng)建對應的object。像Python中其他大多數(shù)情況一樣柱查,我們也可以手動創(chuàng)建這個class object廓俭。這一操作可以通過type()實現(xiàn)。

通常情況下我們可以調用type來得到一個object的類型是什么唉工。如下面的例子所示:

>>> print(type(1))
<type 'int'>

>>> print(type('str'))
<type 'str'>

>>> print(type(example()))
<class '__main__.example'>

>>> print(type(example))
<type 'type'>

在這里我們看到我們所創(chuàng)建example類的type是'type'研乒。這實際上也就是接下來要討論的內(nèi)容。既type的完全不同的功能——type可以動態(tài)創(chuàng)建class淋硝。type()函數(shù)可以接收class的描述來作為參數(shù)并返回所生成的class object雹熬。type同時具有這兩個迥異的功能是由于Python兼容性問題導致的。在此我們不做深究谣膳。

當使用type創(chuàng)建class時竿报,其用法如下:

type(class_name, tuple_of_parent_class, dict_of_attribute_names_and_values)

其中第二個參數(shù)tuple_of_parent_class用來表示繼承關系,可以為空继谚。第三個參數(shù)用來描述我們所要創(chuàng)建的類所應該具有的attribute烈菌。如下面的例子所示:

>>>class class_example(object):
...     pass

上面定義的這個類可以由如下type函數(shù)創(chuàng)建:

>>>class_example = type('class_example', (), {}) # create a class on the fly
>>>print(class_example)
<class '__main__.class_example'>
>>> print(class_example()) # get a instance of the class
<__main__.class_example object at 0x10e414b10>

在這個例子中,type所接收的第一個參數(shù)'class_example'是該類的類名犬庇,同時我們使用了class_example作為存儲該class object引用的變量僧界。這二者可以不同侨嘀。但一般我們沒有理由采用不同的名字從而使得代碼更加復雜臭挽。

我們也可以使用一個字典來定義所創(chuàng)建的class的attribute:

>>> class_example = type('class_example', (), {'attr': 1})
>>> print(class_example)
<class '__main__.class_example'>
>>> print(class_example.attr)
1
>>> print(class_example())
<__main__.class_example object at 0x10e414a90>
>>> print(class_example().attr)
1

上面的例子中type返回的class等同于下面這個class:

>>> class class_example(object):
...     attr = 1

當然,我們也可以用type返回一個繼承class_example的類:

>>> child_example = type('child_example', (class_example,), {})
>>> print(child_example)
<class '__main__.child_example'>
>>> print(child_example.attr)
1

上面這個例子中type返回的class等同于如下class:

>>> class child_example(class_example):
...     pass

我們甚至可以動態(tài)創(chuàng)建包括方法的類咬腕。只要我們創(chuàng)建好方法并將其賦給相應的attribute即可:

>>> def echo(self):
...     print(self.attr)
...
>>> child_example = type('child_example', (class_example,), {'echo': echo})
>>> hasattr(class_example, 'echo')
False
>>> hasattr(child_example, 'echo')
True
>>> child_example().echo()
1

同樣欢峰,我們也可以先動態(tài)創(chuàng)建一個class,然后再賦給其新的方法:

>>> child_example = type('child_example', (class_example,), {})
>>> def another_method(self):
...     print('another method')
...
>>> child_example.another_method = another_method
>>> hasattr(child_example, 'another_method')
True
>>> child_example().another_method()
another method

綜上所述,Python中的class其實是一個object纽帖,并且我們可以動態(tài)創(chuàng)建class宠漩。事實上這也是我們在使用class關鍵字的時候Python所做的事情。Python通過使用metacalss來實現(xiàn)這一過程懊直。

究竟什么是metaclass扒吁?

metaclass就是Python中用來創(chuàng)建class object的class。我們可以將其看做能夠產(chǎn)生class的類工廠室囊。我們可以通過如下例子理解這個關系:

class = metaclass()
object = class()

從上文中我們知道了type()可以被用來動態(tài)創(chuàng)建class雕崩,這是因為實際上type是一個metaclass。而且type實際上是Python用在在幕后創(chuàng)建所有class的metaclass融撞。

包括int, string, function, class在內(nèi)盼铁,Python中所有的東西都是object,而所有的object都是被相應的class創(chuàng)造的尝偎。我們可以通過__class__的值得知這一點饶火。

>>> age = 24
>>> age.__class__
<type 'int'>

>>> name = 'bob'
>>> name.__class__
<type 'str'>

>>> def foo(): pass
>>> foo.__class__
<type 'function'>

>>> class Bar(object): pass
>>> bar = Bar()
>>> bar.__class__
<class '__main__.Bar'>

那么,這些__class____class__又是什么呢致扯?

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> bar.__class__.__class__
<type 'type'>

可以看出肤寝,所有的class都來自于typetype抖僵,作為metaclass醒陆,創(chuàng)建了以上所有的class object。

type是Python定義好的metaclass裆针。當然刨摩,我們也可以自定義metaclass。

類的metaclass attribute

當定義class的時候世吨,我們可以使用__metaclass__ attribute來指定用來初始化當前class的metaclass澡刹。如下面的例子所示:

class Foo(object):
    __metaclass__ = something
    [other statements...]

如果我們指定了__metaclass__,Python就是使用這個metaclass來生成class Foo耘婚。

當Python試圖創(chuàng)建class Foo的時候罢浇,Python會首先在class的定義中尋找__metaclass__ attribute。如果存在__metaclass__沐祷,Python將會使用指定的__metaclass__來創(chuàng)建class Foo嚷闭。如果沒有指定的話,Python就會使用默認的type作為metaclas創(chuàng)建Foo赖临。

所以胞锰,對于下面這個例子:

class Foo(Bar):
    pass

Python首先在Foo中尋找是否存在__metaclass__ attribute。

如果存在的話兢榨,Python將使用這個metaclass在內(nèi)存中創(chuàng)建一個名字為Foo的class object嗅榕。如果Python

如果class定義中不存在__metaclass__的話顺饮,Python將會尋找MODULE級別的__metaclass__。如果存在的話鳩進行與前述相同的操作凌那。但是只有我們定義的class沒有繼承任何類的情況下兼雄,Python才會在MODULE級別尋找__metaclass__∶钡或者說赦肋,只有當該類是一個舊類的情況下,Python才會在MODULE級別尋找__metaclass__励稳。(關于新類和舊類的區(qū)別金砍,請看這篇文章).

當Python仍然沒有找到__metaclass__時,Python將會使用當前類的母類的metaclass來創(chuàng)建當前類麦锯。在我們上面這個例子中恕稠,Python會使用Foo的母類Bar的metaclass來創(chuàng)建Foo的class object。

同時需要注意的是扶欣,在class中定義的__metaclass__ attribute并不會被子類繼承鹅巍。被子類繼承的是母類的metaclass,也就是母類的.__class__ attribute料祠。比如上面的例子中骆捧,Bar.__class__將會被Foo繼承。也就是說髓绽,如果Bar定義了一個__metaclass__ attribute來使用type()創(chuàng)建Bar的class object(而非使用type.__new__())敛苇,那么Bar的子類,也就是Foo顺呕,并不會繼承這一行為枫攀。

那么問題來了:我們究竟應該在__metaclass__ attribute中定義什么?

答案是:能夠創(chuàng)建class的東西株茶。

那么什么能夠創(chuàng)建class呢来涨?type,或者任何type的子類启盛。

自定義metaclass

metaclass的主要目的是在class被創(chuàng)建的時候對生成的class進行自動的動態(tài)修改蹦掐。

一般來說,這一點主要應用于API僵闯,例如我們想要根據(jù)當前的內(nèi)容創(chuàng)建相匹配的class卧抗。

舉一個簡單的例子如下:我們決定讓當前module下所有的class的attribute的名字都是大寫。要實現(xiàn)這個功能有很多種方法鳖粟。使用__metaclass__就是其中之一社裆。

設置了__metaclass__的話,class的創(chuàng)建就會由指定的metaclass處理牺弹,那么我們只需要讓這個metaclass將所有attribute的名字改成大寫即可浦马。

__metaclass__可以是任何Python的callable时呀,不必一定是一個正式的class张漂。

下面我們首先給出一個使用function作為__metaclass__的例子晶默。

# the metaclass will automatically get passed the same argument 
# that is passed to `type()`
def upper_attr(class_name, class_parents, class_attr):
    '''Return a class object, with the list of its attribute turned into 
    uppercase.
    '''
    # pick up any attribute that doesn't start with '__' and turn it into uppercase.
    uppercase_attr = {}
    for name, val in class_attr.items():
        if name.startswith('__'):
            uppercase_attr[name] = val
        else:
            uppercase_attr[name.upper()] = val

    # let `type` do the class creation
    return type(class_name, class_parents, uppercase_attr)

class Foo(object):
    # this __metaclass__ will affect the creation of this new style class
    __metaclass__ = upper_attr
    bar = 'bar'

print(hasattr(Foo), 'bar')
# False

print(hasattr(Foo), 'BAR')
# True

f = Foo()
print(f.BAR)
# 'bar'

接下來我們通過繼承type的方式實現(xiàn)一個真正的class形式的metaclass。注意如果尚不清楚__new____init__的作用和區(qū)別的航攒,請看這篇文章.

# remember that `type` is actually a just a class like int or str
# so we can inherit from it.

class UpperAttrMetaclass(type):
    '''
    __new__ is the method called before __init__
    It's the function that actually creates the object and returns it.
    __init__ only initialize the object passed as a parameter.
    We rarely use __new__, except when we want to control how the object
    is created.
    For a metaclass, the object created is a class. And since we want to 
    customize it, we need to override __new__.
    We can also do something by overriding __init__ to get customized initialization
    process as well.
    Advanced usage involves override __call__, but we won't talk about this here.
    '''
    def __new__(upperattr_metaclass, class_name, class_parents, class_attr):
        uppercase_attr = {}
        for name, val in class_attr.items():
            if name.startswith('__'):
                uppercase_attr[name] = val
            else:
                uppercase_attr[name.upper()] = val
        return type(class_name, class_parents, uppercase_attr)

但這不是很OOP磺陡。我們直接調用了type而非調用type.__new__。那么OOP的做法如下漠畜。

class UpperAttrMetaclass(type):
    def __new__(upperattr_metaclass, class_name, class_parents, class_attr):
        uppercase_attr = {}
        for name, val in class_attr.items():
            if name.startswith('__'):
                uppercase_attr[name] = val
            else:
                uppercase_attr[name.upper()] = val
        # basic OOP. Reuse the parent's `__new__()`
        return type.__new__(upperattr_metaclass, class_name, class_parents, uppercase_attr)

我們注意到币他,__new__所接收的參數(shù)中有一個額外的upperattr_metaclass。這沒有什么特別的憔狞。如同__init__總是接收調用它的object作為第一個參數(shù)一樣(慣例上用self來命名__init__所接收的第一個參數(shù))蝴悉,__new__總是接收其被定義在內(nèi)的class作為第一個參數(shù),就像類方法總是接收其被定義的class作為第一個參數(shù)一樣(慣例上用cls命名類方法所接收的第一個參數(shù))瘾敢。

清楚起見拍冠,這里給出的例子的變量和方法名都很長。但在實際的應用中簇抵,類似于使用selfcls代替第一個參數(shù)庆杜,我們可以將這些名字替換為更加簡潔的形式:

class UpperAttrMetaclass(type):
    def __new__(cls, cls_name, bases, attr_dict):
        uppercase_attr = {}
        for name, val in attr_dict.items():
            if name.startswith('__'):
                uppercase_attr[name] = val
            else:
                uppercase_attr[name.upper()] = val
        return type.__new__(cls, cls_name, bases, uppercase_attr)

通過應用super,我們可以使得上面這段代碼更加干凈簡潔碟摆,也使得繼承更加容易(我們可能有metaclass繼承別的一些metaclass晃财,而這些metaclass又繼承type):

class UpperAttrMetaclass(type):
    def __new__(cls, cls_name, bases, attr_dict):
        uppercase_attr = {}
        for name, val in attr_dict.items():
            if name.startswith('__'):
                uppercase_attr[name] = val
            else:
                uppercase_attr[name.upper()] = val
        return super(UpperAttrMetaclass, cls).__new__(cls, cls_name, bases, uppercase_attr)

Voilà!上述基本就是關于metaclass的一切了典蜕。

使用metaclass之所以復雜断盛,不是因為其代碼實現(xiàn)復雜,而是因為我們一般使用metaclass來做一些邏輯上很復雜的操作愉舔,例如自省郑临,修改繼承以及改變類的默認attribute如__dict__等。

metaclass的確可以被用來實現(xiàn)一些奇妙的功能屑宠,也因此可以用來進行極其復雜的邏輯操作厢洞。但是metaclass本身是很簡單的:

  • 影響class初始化的過程
  • 修改class的內(nèi)容
  • 返回修改過的class

為什么我們要使用metaclass,而不是使用一些函數(shù)來實現(xiàn)類似的功能典奉?

就像前文所說躺翻,__metaclass__實際上可以是任何callable,那么為什么我們還要使用metaclass而不是直接調用這些函數(shù)呢卫玖?

使用class作為metaclass有如下幾個理由:

  • 使用class作為metaclass能夠使得我們代碼的動機更加明確公你。比如當我們讀到上面所定義的UpperAttrMetaclass(type)代碼時,我們清楚地知道接下來這段代碼想要干什么(自定義class object初始化的過程)假瞬。
  • 我們能夠使用OOP的思想進行處理陕靠。class作為metaclass可以繼承其他的metaclass迂尝,重載母類的方法,甚至可以使用別的metaclass剪芥。
  • 如果我們使用class作為metaclass垄开,某一使用該metaclass的class的子類將仍是是其metaclass的實例。但這一功能無法通過使用函數(shù)作為metaclass實現(xiàn)税肪。
  • 使用metaclass可以使得代碼結構更加優(yōu)美溉躲。實際應用中我們很少使用metaclass來實現(xiàn)上面那樣簡單的功能。使用metaclass往往是為了實現(xiàn)非常復雜的操作益兄。如果使用class作為metaclass锻梳,我們就可以把相應的方法封裝到這一個metaclass中,使得代碼更加易懂净捅。
  • 使用class作為metaclass可以在class中容易的定義__new__疑枯,__init____call__方法蛔六。雖然我們在將所有的邏輯都放入__new__中荆永,但有的時候根據(jù)需要使用其他幾個方法會使得邏輯更加清晰。
  • 額賊古今!人家名字就叫metaclass屁魏。這不是帶著個class嗎?

為什么我們要使用metaclass呢捉腥?

那么究竟為什么我們要使用metaclass這樣一個難以理解且容易出錯的實現(xiàn)方式呢氓拼?

答案是通常情況下我們不需要使用metaclass。

引用Python大師Tim Peters的話來說抵碟,就是:

Metaclasses are deeper magic that 99% of users should never worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).

metaclass主要的使用情況就是用來創(chuàng)建API桃漾。使用metaclass的一個典型的例子是Django ORM。

它是的我們可以使用如下當時定義一個model:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

同時拟逮,如果我們調用這個model:

guy = Person(name='bob', age='35')
print(guy.age)

其并不會返回一個IntegerField對象撬统,而是會返回一個int,甚至可以直接從數(shù)據(jù)庫中調用這個值敦迄。

正是因為models.Model定義了__metaclass__恋追,并使用了一些操作來將我們使用簡單的語句定義的Person轉化成了與數(shù)據(jù)庫相應的域相聯(lián)系的類,這種邏輯才成為可能罚屋。

Django使得很多復雜的邏輯僅暴露一個簡單的API接口就可以調用苦囱,這正是通過metaclass實現(xiàn)的。metaclass會根據(jù)需要重新實現(xiàn)這些復雜操作所需要的真正的代碼脾猛。

再說兩句

首先我們知道了Python中的class實際上是object撕彤,同時class仍具有創(chuàng)建對應的實例的能力。

實際上class本身也是metaclass的實例猛拴。

>>> class Foo(object):
...     pass
...
>>> id(Foo)
4299321816

Python中的任何東西都是object羹铅,這些object不是class的實例就是metaclass的實例蚀狰。

當然,type除外职员。

type事實上是其自身的metaclass麻蹋。我們使用Python是無法重復這種實現(xiàn)的。這一邏輯是在Python代碼實現(xiàn)的層面定義的廉邑。引用一下道德經(jīng)中的說法哥蔚,我們可以說Python中type生metaclass倒谷,metaclass生class蛛蒙,class生萬物

另外渤愁,metaclass的應用一般頗為復雜牵祟,大多數(shù)情況下我們可以使用別的方法實現(xiàn)相同的功能。比如我們可以通過一下兩種技術修改class:

  • monkey patching
  • class decorators

99%我們需要改變class的情況下抖格,我們使用上述兩種技術可以解決诺苹。

但事實是,99%的情況下我們根本不需要改變class雹拄。

作者:耀凱考前突擊大師
鏈接:http://www.reibang.com/p/224ffcb8e73e
來源:簡書
著作權歸作者所有收奔。商業(yè)轉載請聯(lián)系作者獲得授權,非商業(yè)轉載請注明出處滓玖。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坪哄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子势篡,更是在濱河造成了極大的恐慌翩肌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件禁悠,死亡現(xiàn)場離奇詭異念祭,居然都是意外死亡,警方通過查閱死者的電腦和手機碍侦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門粱坤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瓷产,你說我怎么就攤上這事站玄。” “怎么了拦英?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵蜒什,是天一觀的道長。 經(jīng)常有香客問我疤估,道長灾常,這世上最難降的妖魔是什么霎冯? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮钞瀑,結果婚禮上沈撞,老公的妹妹穿的比我還像新娘。我一直安慰自己雕什,他們只是感情好缠俺,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贷岸,像睡著了一般壹士。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上偿警,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天躏救,我揣著相機與錄音,去河邊找鬼螟蒸。 笑死盒使,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的七嫌。 我是一名探鬼主播少办,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼诵原!你這毒婦竟也來了英妓?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤皮假,失蹤者是張志新(化名)和其女友劉穎鞋拟,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惹资,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡贺纲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了褪测。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猴誊。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖侮措,靈堂內(nèi)的尸體忽然破棺而出懈叹,到底是詐尸還是另有隱情,我是刑警寧澤分扎,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布澄成,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏墨状。R本人自食惡果不足惜卫漫,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肾砂。 院中可真熱鬧列赎,春花似錦、人聲如沸镐确。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽源葫。三九已至诗越,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間臼氨,已是汗流浹背掺喻。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工芭届, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留储矩,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓褂乍,卻偏偏與公主長得像持隧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逃片,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

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