python之理解元類

python之理解元類

1桥滨、類也是對象

在大多數(shù)編程語言中摆碉,類就是一組用來描述如何生成對象的代碼段。在python中顾画,這一點仍然成立取劫。

class BaseObject(object):
    pass


demo_object = BaseObject()
print(demo_object)
print(BaseObject)

結(jié)果:

<__main__.BaseObject object at 0x0000000001DC8780>
<class '__main__.BaseObject'>

demo_object顯而易見的是一個實例對象,那類BaseObject也是一個對象研侣?對的谱邪,是一個對象,只要你使用關(guān)鍵字class庶诡,python解釋器在執(zhí)行的時候就會創(chuàng)建這個一個對象惦银,如上:<class '__main__.BaseObject'>

解釋器將在內(nèi)存中創(chuàng)建一個對象末誓,名字就叫做BaseObject扯俱。這個對象(類對象BaseObject)擁有創(chuàng)建對象(實例對象)能力。但是喇澡,它的本質(zhì)仍舊是一個對象蘸吓,針對對象類型,我們通常有下面的一些操作:

  • 將對象復(fù)制給一個變量
  • 拷貝對象
  • 為對象增加屬性
  • 將對象作為函數(shù)參數(shù)傳遞
class BaseObject(object):
    pass


def echo(o):
    print(o)


echo(BaseObject)
print(hasattr(BaseObject, 'new_attr'))
BaseObject.new_attr = 'daocoder'
print(hasattr(BaseObject, 'new_attr'))
print(BaseObject.new_attr)
a = BaseObject
print(a)

結(jié)果:

<class '__main__.BaseObject'>
False
True
daocoder
<class '__main__.BaseObject'>

2撩幽、動態(tài)的創(chuàng)建類

因為類也是對象,那么你可以在運行的時候動態(tài)創(chuàng)建他們箩艺,就像一般實例化對象一樣窜醉。那么,你可以在函數(shù)中創(chuàng)建類艺谆,使用class關(guān)鍵字即可榨惰。

def create_object(name):
    if name == 'Foo':
        class Foo(object):
            pass
        return Foo
    elif name == 'Bar':
        class Bar(object):
            pass
        return Bar


MyClass = create_object('Foo')
print(MyClass)
print(MyClass())

結(jié)果:

<class '__main__.create_object.<locals>.Foo'>
<__main__.create_object.<locals>.Foo object at 0x0000000002B745F8>

由運行結(jié)果可以看出確實可以實現(xiàn)這樣的需求,但是這樣還是需要自己編寫整個代碼静汤,不夠動態(tài)琅催,那么我們可以想到對于一門語言,肯定可以自動創(chuàng)建對象虫给,既然類也是一個對象藤抡,那么它肯定也是通過什么創(chuàng)建出來的。

當我們利用class關(guān)鍵字時抹估,python解釋器會自動操作這個類對象缠黍,那么和python實現(xiàn)的大多數(shù)事情一樣,python也提供了手動創(chuàng)建的方法药蜻。

回歸到我們學(xué)習(xí)python函數(shù)的基礎(chǔ)瓷式,有一個內(nèi)建函數(shù)用來獲取各個變量的類型type()替饿,就像下面這樣。

print(type(1))
print(type('1'))
print(type(True))
print(type(range(1)))
print(type([]))
print(type({}))
print(type(()))
print(type(MyClass))

結(jié)果:

<class 'int'>
<class 'str'>
<class 'bool'>
<class 'range'>
<class 'list'>
<class 'dict'>
<class 'tuple'>
<class 'type'>

注意看最后一個贸典,MyClass的類型竟然是type视卢。那么這個type到底是啥東西。

3廊驼、利用type創(chuàng)建類

type還有一個完全不同的功能据过,就是動態(tài)創(chuàng)建類。

type可以接受一個類的描述符作為參數(shù)蔬充,然后返回一個類蝶俱。(通常根據(jù)傳入?yún)?shù)的不同,同一個函數(shù)可以擁有兩種完全不同的用法饥漫,是很忌諱的事情榨呆,但python這里是為了保持向后兼容性)。

所以庸队,type可以像下面這樣工作:

type(類名, 由父類名稱組成的元組(繼承情況积蜻,可為空), 包含字典的屬性(名稱和值))

如下面的代碼:

class Test(object):
    pass


print(Test)

Dog = type('Dog', (), {})
print(Dog)

結(jié)果:

<class '__main__.Test'>
<class '__main__.Dog'>

再看幫助:

help(Test)
help(Dog)

結(jié)果:

Help on class Test in module __main__:

class Test(builtins.object)
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

Help on class Dog in module __main__:

class Dog(builtins.object)
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

一樣的彻消。

4竿拆、利用type創(chuàng)建帶有屬性、方法的類

1宾尚、創(chuàng)建帶屬性的類

Animal = type('Animal', (), {'name': 'daocoder'})
animal = Animal()
print(animal.name)

結(jié)果:

daocoder

2丙笋、創(chuàng)建帶屬性、方法且繼承的類

Animal = type('Animal', (), {'name': 'daocoder'})
animal = Animal()
print(animal.name)


def talk(obj, age):
    print('my name is %s and %s years old' % (obj.name, age))


Dog = type('Dog', (Animal,), {'name': 'huang', 'talk': talk})
dog = Dog()
# dog.name = 'da huang'
dog.talk(10)

結(jié)果:

daocoder
my name is huang and 10 years old

這里注意:

  • type的第2個參數(shù)煌贴,元組中是父類的名字御板,不是字符串。
  • 添加的屬性是類屬性牛郑,不是實例屬性怠肋。

3、添加靜態(tài)方法和類方法

Animal = type('Animal', (), {'name': 'daocoder'})
animal = Animal()
print(animal.name)


def talk(obj, age):
    print('my name is %s and %s years old' % (obj.name, age))


@staticmethod
def eat(food):
    print('i can eat %s' % food)


@classmethod
def sleep(self, dt):
    print('%s can sleep %s hours' % (self.name, dt))


Dog = type('Dog', (Animal,), {'name': 'huang', 'talk': talk, 'eat': eat, 'sleep': sleep})
dog = Dog()
# dog.name = 'da huang'
dog.talk(10)
dog.eat('meat')
dog.sleep(8)

結(jié)果:

daocoder
my name is huang and 10 years old
i can eat meat
huang can sleep 8 hours

5淹朋、什么是元類

元類就是用來創(chuàng)建類(對象)的笙各,可以理解為元類就是創(chuàng)建類的類。

MyClass = type('MetaClass', (), {})
# class MyClass(object):
#     pass


MyObject = MyClass()

print(MyClass)
print(MyObject)

結(jié)果:

<class '__main__.MetaClass'>
<__main__.MetaClass object at 0x00000000023E9D30>

上面利用type創(chuàng)建了一個MyClass類(對象)础芍,然后基于它實例化一個對象MyObject杈抢。注釋的基本形式結(jié)果一致。

這里實際上type函數(shù)就是一個元類仑性。type就是在python解釋器用來創(chuàng)建所有類的元類春感。我們可以這么理解,類比str是創(chuàng)建字符串對象的類(對象),int是創(chuàng)建整數(shù)對象的類(對象)鲫懒,那么type就是創(chuàng)建對象的類(對象)嫩实。python中,所有的東西都是對象窥岩,包括整數(shù)甲献、字符串、函數(shù)及類颂翼。它們?nèi)菍ο蠡稳鳎覐囊粋€類中創(chuàng)建而來,這個類就是type朦乏。

print(type(1))
print(type('1'))
print(type(True))

print(type(int.__class__))
print(type(str.__class__))
print(type(bool.__class__))
print(type(object.__class__))

print(type(int.__class__.__class__))
print(type(str.__class__.__class__))
print(type(bool.__class__.__class__))
print(type(object.__class__.__class__))

結(jié)果:

<class 'int'>
<class 'str'>
<class 'bool'>

<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>

<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>

從上面可以看出元類就是創(chuàng)建類這種對象的東西球及,type是python內(nèi)建的元類,那么我們當然可以創(chuàng)建自己的元類啊呻疹。

6吃引、metaclass屬性

我們可以定義一個類的時候為其添加一個metaclass屬性。

class Bar(type):
    pass


class Foo(object, metaclass=Bar):
    # __metaclass__ = Bar # python2的寫法
    pass

當我們運行這段代碼時刽锤,python進行了如下操作:

1镊尺、Foo中有metaclass的屬性么?如有并思,py將通過metaclass創(chuàng)建一個名字為Foo的類(對象)
2庐氮、如果沒有找到metaclass,它會繼續(xù)在其繼承的父類中去尋找metaclass屬性宋彼,并嘗試和之前相同的操作弄砍。
3、如果python在父類中都找不到metaclass输涕,python會在模塊層次去尋找metaclass输枯,并嘗試做和前面一樣的操作。
4占贫、如果還是找不到__metaclass,那么python將會用內(nèi)置的type去創(chuàng)建這個對象先口。

那么問題就顯而易見了型奥,metaclass指什么,答案就是可以創(chuàng)建一個類的東西碉京。創(chuàng)建一個類需要type或繼承自type的子類(對象)厢汹。

7、自定義元類

元類的主要目的就是為了創(chuàng)建類的時候能夠自動的改變類谐宙。

舉一個幫助理解的例子:你決定在你的模塊里所有的類的屬性都應(yīng)該是大寫形式烫葬。有一些辦法可以辦法,其中一種就是在模塊級別設(shè)定metaclass。采用這種方法搭综,這個模塊中的所有類都會通過這個元類來創(chuàng)建垢箕,我們需要做的就是在元類中把所有索性全部改為大寫就可以了。

值得一提的是兑巾,metaclass實際上可以被任意調(diào)用条获,它并不需要一個正式的類,所以下面先以一個函數(shù)來開始蒋歌。

def upper_attr(class_name, parent_class, attr):
    new_attr = {}
    print(class_name)
    print(parent_class)
    print(attr)
    for name, value in attr.items():
        if not name.startswith('__'):
            new_attr[name.upper()] = value
    return type(class_name, parent_class, new_attr)


class Bar(object):
    pass


class MyClass(object):
    pass


class Foo(MyClass, Bar, metaclass=upper_attr):
    # __metaclass__ = Bar
    test = 'test'


foo = Foo()
print(foo)
print(hasattr(foo, 'test'))
print(hasattr(foo, 'TEST'))

結(jié)果:

Foo
(<class '__main__.MyClass'>, <class '__main__.Bar'>)
{'__module__': '__main__', '__qualname__': 'Foo', 'test': 'test'}
<__main__.Foo object at 0x0000000002BA4748>
False
True

如上面代碼帅掘,class_name是當前類名,parent_class為要繼承的父類堂油,attr為字典形式所有的類屬性修档。

下面再考慮用一個真正的class作為元類。

class Bar(object):
    pass


class MyMetaClass(type):
    def __new__(cls, class_name, class_parents, class_attr):
        print(cls)
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith('__'):
                new_attr[name.upper()] = value
            # return type(class_name, class_parents, new_attr)
            return type.__new__(cls, class_name, class_parents, new_attr)


class Foo(Bar, metaclass=MyMetaClass):
    # __metaclass__ = Bar
    test = 'test'


foo = Foo()
print(foo)
print(hasattr(foo, 'test'))
print(hasattr(foo, 'TEST'))

從上面的代碼可以理解府框,元類的作用基本就是攔截類的創(chuàng)建吱窝,然后修改這個待創(chuàng)建的類,最后返回修改后的類寓免。

下一篇會理解new癣诱、initcall等魔術(shù)方法袜香。

最后為什么要使用元類撕予,用python界的領(lǐng)袖Tim Peters的話來說:元類是深度的魔法,99%的人應(yīng)該不為此操心什么蜈首。如果你想搞清楚是否需要用到元類实抡,那么就不需要用到它。那些實際用到元類的人很清楚他們需要做什么欢策,而且根本不需要去解釋為什么要用元類吆寨。

8、利用元類實現(xiàn)ORM

8.1 ORM是什么

ORM(object relational mapping)對象關(guān)系印射踩寇,是py后端框架django的核心思想啄清。

通俗點理解就是通過創(chuàng)建一個實例對象,用創(chuàng)建它的類當作表名俺孙,用創(chuàng)建它的類屬性作為表的字段辣卒,當對這個實例對象進行操作時,能夠生成對應(yīng)的sql語句睛榄。

下面一個簡單的demo:

class User(object):
    uid = ('uid', "int unsigned")
    name = ('username', "varchar(30)")
    email = ('email', "varchar(30)")
    password = ('password', "varchar(30)")


u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
u.save()
# 對應(yīng)如下sql語句
# insert into User (username, email, password, uid) values ('Michael', 'test@orm.org', 'my-pwd', 12345)

將實現(xiàn)的效果如上荣茫,即ORM的作用就是:讓開發(fā)者操作數(shù)據(jù)庫的時候,能夠像操作對象時通過其屬性賦值等操作一樣簡單场靴。

8.2 通過元類實現(xiàn)ORM中的insert功能

編寫底層模塊的第一步就是先把調(diào)用接口寫出來啡莉,比如想寫一個ORM框架港准,想定義一個User類來操作相應(yīng)的數(shù)據(jù)庫表user,需要開發(fā)者寫的代碼類似這個樣子:

class User(Model):
    id = IntegerField('id')
    name = StringField('name')
    age = IntegerField('age')
    address = StringField('address')


# 創(chuàng)建一個user實例
user = User(id=1, name='daocoder', age=27, address='anhui')
# 保存到數(shù)據(jù)庫中
user.save()

其中父類Model和屬性類型IntegerField和StringField都是由ORM框架提供咧欣,剩下的方法由metaclass完成浅缸,即創(chuàng)建Model的元類。

下面就按照上面的思路來實現(xiàn)這個簡單的ORM框架该押。

1疗杉、首先定義Field類,它負責(zé)定義數(shù)據(jù)表中字段名和字段類型長度等蚕礼。

class Field(object):
    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)

2烟具、在Field的基礎(chǔ)上再定義各種數(shù)據(jù)類型,StringField和IntegerField等奠蹬。

class IntegerField(Field):
    def __init__(self, name, column_type='int(11)'):
        super(IntegerField, self).__init__(name, column_type)


class StringField(Field):
    def __init__(self, name, column_type='varchar(100)'):
        super(StringField, self).__init__(name, column_type)

3朝聋、下面開始定義基類Model模型。

class Model(dict, metaclass=ModelMetaClass):

    def __init__(self, **kwargs):
        super(Model, self).__init__(**kwargs)

    def __getattr__(self, key):
        try:
            return str(self[key])
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = str(value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            print(k, v, v.name, v.column_type)
            fields.append(v.name)
            args.append(getattr(self, k))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

4囤躁、編寫元類ModelMetaclass

class ModelMetaClass(type):
    def __new__(cls, class_name, class_parents, class_attr):
        if class_name == 'Model':
            return type.__new__(cls, class_name, class_parents, class_attr)
        print('found model %s' % class_name)
        mappings = {}
        for name, value in class_attr.items():
            if isinstance(value, Field):
                mappings[name] = value
        for k in mappings.keys():
            class_attr.pop(k)
        class_attr['__mappings__'] = mappings
        class_attr['__table__'] = class_name.lower()
        return type.__new__(cls, class_name, class_parents, class_attr)

全部代碼:

class Field(object):
    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)


class IntegerField(Field):
    def __init__(self, name, column_type='int(11)'):
        super(IntegerField, self).__init__(name, column_type)


class StringField(Field):
    def __init__(self, name, column_type='varchar(100)'):
        super(StringField, self).__init__(name, column_type)


class ModelMetaClass(type):
    def __new__(cls, class_name, class_parents, class_attr):
        if class_name == 'Model':
            return type.__new__(cls, class_name, class_parents, class_attr)
        print('found model %s' % class_name)
        mappings = {}
        for name, value in class_attr.items():
            if isinstance(value, Field):
                mappings[name] = value
        for k in mappings.keys():
            class_attr.pop(k)
        class_attr['__mappings__'] = mappings
        class_attr['__table__'] = class_name.lower()
        return type.__new__(cls, class_name, class_parents, class_attr)


class Model(dict, metaclass=ModelMetaClass):

    def __init__(self, **kwargs):
        super(Model, self).__init__(**kwargs)

    def __getattr__(self, key):
        try:
            return str(self[key])
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = str(value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            print(k, v, v.name, v.column_type)
            fields.append(v.name)
            args.append(getattr(self, k))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))


class User(Model):
    id = IntegerField('id')
    name = StringField('name')
    age = IntegerField('age')
    address = StringField('address')


user = User(id='1', name='daocoder', age=27, address='anhui')
user.save()

跑起來:

found model User
id <IntegerField:id> id int(11)
name <StringField:name> name varchar(100)
age <IntegerField:age> age int(11)
address <StringField:address> address varchar(100)
SQL: insert into user (id,name,age,address) values (1,daocoder,27,anhui)
ARGS: ['1', 'daocoder', '27', 'anhui']

解釋一番:

User類定義繼承自父類Model冀痕,且有4個屬性,4個屬性分別繼承自IntegerField和StringField狸演,這兩個繼承自Field言蛇,這個不談。聚焦Model。

1、實例化User時局荚,去找父類Model低淡,發(fā)現(xiàn)父類擁有metaclass屬性值為ModelMetaClass父腕,即它是由一個自定義的元類來創(chuàng)建的類,向上尋找ModelMetaClass,這個類是繼承自type。需要先創(chuàng)建它的實例對象民宿。調(diào)用其靜態(tài)方法new,這里面4個參數(shù)(cls, class_name, class_parents, class_attr)像鸡,分別為ModelMetaClass的類對象活鹰、Model類名、父類(dict, )元組只估、自身內(nèi)置屬性志群。類名為Model時,直接創(chuàng)建type.__new__(cls, class_name, class_parents, class_attr)并返回仅乓。再調(diào)用Model類的init方法,調(diào)用了父類dict的init的方法蓬戚。父類Model作為類對象創(chuàng)建完成夸楣。

2、開始User類對象的創(chuàng)建,Model已有豫喧,然后開始創(chuàng)建User石洗,還是向上找到了ModelMetaClass,這時的4個參數(shù)分別是(cls, class_name, class_parents, class_attr)紧显,分別為ModelMetaClass的類對象讲衫、User類名、父類(Model, )元組孵班、自身內(nèi)置屬性包含id涉兽,name,age篙程,address等枷畏。然后判斷類名不是Model,繼續(xù)向下虱饿,將User屬性遍歷拥诡,其實例自Field的屬性封裝為User類對象的mappings屬性,類名User為User類對象的table屬性氮发。

3渴肉、調(diào)用實例對象user.save方法,沒啥可說的了爽冕,調(diào)用類對象獲取其屬性的內(nèi)置方法仇祭。

9、致謝

python 簡易ORM

某平臺內(nèi)部培訓(xùn)資料扇售。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末前塔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子承冰,更是在濱河造成了極大的恐慌华弓,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件困乒,死亡現(xiàn)場離奇詭異寂屏,居然都是意外死亡,警方通過查閱死者的電腦和手機娜搂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門迁霎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人百宇,你說我怎么就攤上這事考廉。” “怎么了携御?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵昌粤,是天一觀的道長既绕。 經(jīng)常有香客問我,道長涮坐,這世上最難降的妖魔是什么凄贩? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮袱讹,結(jié)果婚禮上疲扎,老公的妹妹穿的比我還像新娘。我一直安慰自己捷雕,他們只是感情好椒丧,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著非区,像睡著了一般瓜挽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上征绸,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天久橙,我揣著相機與錄音,去河邊找鬼管怠。 笑死淆衷,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的渤弛。 我是一名探鬼主播祝拯,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼她肯!你這毒婦竟也來了佳头?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤晴氨,失蹤者是張志新(化名)和其女友劉穎康嘉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體籽前,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡亭珍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了枝哄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肄梨。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖挠锥,靈堂內(nèi)的尸體忽然破棺而出众羡,到底是詐尸還是另有隱情,我是刑警寧澤蓖租,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布粱侣,位于F島的核電站辆毡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏甜害。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一球昨、第九天 我趴在偏房一處隱蔽的房頂上張望尔店。 院中可真熱鬧,春花似錦主慰、人聲如沸嚣州。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽该肴。三九已至,卻和暖如春藐不,著一層夾襖步出監(jiān)牢的瞬間匀哄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工雏蛮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涎嚼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓挑秉,卻偏偏與公主長得像法梯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子犀概,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

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