python个人总结

基础

同所有的语言一样,Python都有它定义类型的方式,引入第三方库的方式,输入输出等等基本操作。这些可以在这里查到。

闲话

import的时候会执行模块内容,比如里面要是有print会输出
if __name__ == __main__来区分是否是主模块,还只是非主模块。

is 和 == 区别

is 用于判断两个变量引用对象是否为同一个, ==用于判断引用变量的值是否相等。

内置函数参考一览

字符串之中文处理“解决中文乱码问题”

#!/usr/local/bin/python
#encoding:utf-8 | #-*-coding:utf8-*- | coding:utf8

1. 简介

字符串的编码是将人能看懂的字符转化成只有机器能看懂的01机器码,于是这里就牵扯了字符串的编码问题了。这个问题为什么要单独拿出来说呢?那是因为在不同的编码的环境下,中文字符都可能乱码,如果你的环境是`utf-8`的环境,而终端是`GBK`环境,那么两个环境之一必将乱码。这是一个比较头疼的现象。存文件的时候也是,不同的编码最终存的机器码是不同的。
**注:**Python2中支持str和unicode,而Python3中**只支持**unicode。

2. 编码的演变方式如下:

ASCII -> ISO-8859-1 -> GB2 -> GBK -> GB18030
#ASCII是一个字节编码一个字符只支持英文名,
#ISO-8859是对ASCII的一个拓展,还是一个字节编码一个字符,支持西欧的国家
#带GB的都是支持中文的,两字节编一个字,GB的互相兼容
unicode -> UTF-8 -> UTF-32
#之后的统一是unicode,但是存储效率太低。
#对unicode进行压缩后是UTF-8和UTF-32。

3. 解决方案

import sys
print sys.stdin.encoding
print sys.stdout.encoding

x = raw_input(u'请输入名字:'.encode(sys.stdout.encoding))
f = open('1.txt','w')
f.write(x.decode(sys.stdin.encoding).encode('utf-8'))
f.close()

#这样子我们就让文字在不同的环境能正确显示。
#同时用解码和编码的方式把存下来的内容按UTF-8的方式存储下来了

变量作用域

1. LEGB原则

'''python的变量按照以下的顺序进行搜索
L(local):函数本地
E(enclose):任意上层的嵌套函数
G(global):全局作用域(模块,文件内)
B(build-in):内置作用域(夸文件)
'''

x = 10
def fun():
    global x
    x += 2
    pass
#这样子就在函数里面调用了函数外的变量

函数的嵌套和闭包

闭包:能够保留函数定义时的环境信息
嵌套:内部函数用了外部函数的变量,或者外部函数返回了内部函数

def f(x):
    y = 100
    def inner(z):
        return x * y + z
    return inner

a10 = f(10)#在创建的时候,给f这个函数的x赋值了10

print(a10(29)) #在调用函数内部函数inner输出结果为1029
#这时候我们观察到本来应该在调用后消失的x的值,在下面一步调用中还是保留着的。
#这种情况我们称之为闭包,即在a10创建的时候保存了定义时的环境变量
#这种做法我们能够更灵活的使用函数

生成器

生成器的定义为:能生成迭代器的东西
生成器表达式:

(expr for iter in iterable [if condition])

生成器函数:
如果函数的返回值用的不是return而是yield则是生成器函数,能用for函数使用。

def inc(n = 1):
    while True:
        yield n
        n += 1

a = inc()
print(a.next())
#可以变相的认为在yield的地方被打了一个端点。
#用yield放回的时候,函数的堆栈还在保存着没有销毁

Why yield?

当一个函数 f,f 返回一个结果,而这个结果是动态计算出来的,同时这个结果如果很大。这个时候,我们希望每次调用这个函数并使用迭代器进行循环的时候一个一个的得到每个 list 元素而不是直接得到一个完整的结果来节省内存,这个时候yield就很有用。

打个比方的话,yield有点像断点。 加了yield的函数,每次执行到有yield的时候,会返回yield后面的值 并且函数会暂停,直到下次调用或迭代终止;
yield后面可以加多个数值(可以是任意类型),但返回的值是元组类型的。
具体怎么使用 yield 参考:Python yield 使用浅析

集合操作(这个在其他语言中不常见特此记录)

1. Python中的集合分类

set         #可变集合
frozenset   #不可变集合,能hash

2. 运算

s | t       #并集
s & t       #交集
s - t       #差集
s ^ t       #对称差集,抛去相交的剩下的
s |= t      #把t并入s
s < t       #看s是否为t的子集
s > t       #看s是否为t的超集,即t包含于s
s.isdisjoint(t) #看s和t是否有交集

然后以下是可变集合特有的操作
python s.add(item) s.clear() s.dicard(item) && s.remove(item) #都是删除操作,如果不存在,remove会抛出异常,discard不会抛出异常 s.update(t) #和t对比,把没有的追加进来 s.difference_update(t) #在s中删除和t交集的部分

装饰器

本质是函数的嵌套调用
目的是不破坏原有的代码的前提下,进行调试

#运行逻辑如下
#decorator(f)(*args,**kwargs)
@decorator
def f(): pass
#这个deorator可以换成任何名字

#decorator(name)(f)(*args,**kwargs)
@decorator(name)
def f():pass
#带参数的函数

def log(func):
    def wrapper(*args, **kwargs): #这么写是可以通配所有的函数
        print('~'*40)
        start = time.clock()
        res = func(*args, **kwargs)
        end = time.clock()
        print('calling',func.__name__,args,kwargs)
        print('start at',start,' end at',end)
    return wrapper
#以上就是对装饰器的定义

def logEx(name):
    def wrapper(func):
        def wrapper1(*args,**kwargs):
            print('~'*40)
            start = time.clock()
            res = func(*args, **kwargs)
            end = time.clock()
            print(name,'calling',func.__name__,args,kwargs)
            print('start at',start,' end at',end)
        return wrapper1
    return wrapper

@logEx('Tom')
@log
#装饰器可以连续插入新的功能,不会影响原来的业务功能
def f(x,y):
    return x+y
#这样子返回的是测试者姓名,函数的输入参数,还有输出的结果

列表和列表解析(常用的操作这里不说,仅记录自己不太熟悉的操作)

1. 列表常用操作

append          #加入元素
extend          #合并列表
del             #删除
enumerate(iter) #返回一个迭代器,会返回索引和值
"""
example for enumerate
for (index, value) in enumerate(itr):
    print index,value
"""
len                 #返回长度
reversed            #反置操作
sorted(x,key=abs)   #排序操作,高度可定制
sum                 #求和操作

2. 列表解析

列表解析是根据一个列表生成一个新的列表,几乎可以和for循环等价,但是它的优点为代码紧凑。同时性能更好

[expr for iter in iterable [if condition]]
 """
 result = [i**2 for in x if i<0]
 result = [i**2 if i<0 else i for in x]
 """

迭代器

  • 可迭代对象(Iterable):
    如果一个对象可以用for ... in ...的方式遍历内容,则其为可迭代
  • 迭代器(Iterator):
    遍历可迭代对象内容的方式
  • itertools:
    • permutations:排列
    cite = itertools.permutations(x,3)
    for c in cite:
        print(c)
    #对x里面的内容进行3个3个进行排列
  • combinations:组合
        itertools.combinations(x,3)
        #对x里面的内容进行3个3个进行组合
  • product:笛卡尔积
        z = ['a','b','c']
        y = range(1,4)
        a = itertools.product(z,y)
        for i in a:
            print(i)
  • repeat:重复
  • chain:链接一组迭代器
        #把多个迭代器塞在一起
        itertools.chain(citer,piter,priter)
    

面向对象编程思想

面向对象编程的特点

1. 封装
相当于一个黑匣子,功能的实现过程封装了进去。
2. 继承
字面意思,就是从父类中继承了他们的功能。
3. 多态
继承了不同的父类,虽然他们的是同一个类但是由于继承了不同的父类,呈现了不同的功能,呈现了多样性。

类的简单说明

1. 经典类
2. 新式类
`__new__`
`__super`
`__slot__`

类的使用

class A:
    pass
#不继承任何类
class B(object):
    pass
#继承的类向上推最终为object的话,这个类就是新式类
class D(B):
    pass
#如D类也是新式类

类的定义

1. 属性
class Chinese(Person):
    nation = 'China'
    def __init__(self, name):
        self.__name = name
    #self指的是这个类自己,同理java和Cpp的this
    #前后都有`__`的是已经有的方法
    #用self来操作的属性是对类里面的属性进行操作的。
    #属性前面有`__`的属性是私有属性,没有的话是公有属性
    def msg(self):
        print(self.name)

a = Chinses(myname) #这样创建实例
a.__dict__ #来看有哪些属性
a.nation = 'blabla' #这样子来修改
a._Chinese__name = 'blabla'
#这个证明用`__`定义的私有属性只是改了名字来保护属性,但是其实是有规律的

类属性:
这个是类本身的属性,在创建实例的时候不需要单独在实例分配内存的属性。如上述例子中的nation属性。如果修改了类的这个属性,那么所有用这个类创建的实例中的这个属性都会被更改。
实例属性:
__init__初始函数中定义的属性是实例属性。这个属性是在类创建实例的时候生成的属性,这个是要单独分配内存的,而不是类本身就有的属性。

2. 属性的可见性
在其他语言如`Cpp`和`Java`中都有特定的结构如`private`和`protected`来定义,但是python其实是没有底层对他们进行实现的,所以python的共有属性和私有属性**是伪的**,所有的都可以用`__dict__`方式进行查到的。
**公有属性**
**私有属性 **:
我们在属性前加`_`或者`__`来标记这个是私有属性。
- `__`定义的属性:
    是会被python**通过改名换姓的方式进行保护。**
    - `_`定义的属性:
        只是**形式上的私有属性**,提醒你这个是私有属性不要尝试去直接访问它。
3. 访问属性

直接访问
这个不是一个好的方式,因为违背了封装性。坏处是如果类的属性名称进行了更改,那所有直接访问这个属性的代码都需要更改。

定义get,set方法:
这个方法就是CppJava中常用的方法。

通过属性装饰器
- @property:属性的查看
- @***.setter:属性的写入
- @***.deleter:属性的删除

    class Chinese(Person):
            nation = 'China'
            def __init__(self, id, name):
                self.__name = name
                self.__id = id
                slef.__email = None
            def getID(self):
                print(self.__id)
            
            @property
            def name(self):
                return self.__name
            @name.setter
            def name(self, name):
                self.__name = name
            @name.deleter
            def name(self):
                del self.name

    VDeamoV = Chinese(1,'VDeamoV')
    print(VDeamoV.getID())
    VDeamoV.name = 'Other Name' 
    print(VDeamoV.name)
    #这里替换了并输出了保护的属性,但是没有用类中的名字

这个好处就是,使用的人调用的时候看起来就像函数调用的感觉,而不是调用函数的感觉。

4. 通过描述符访问

编写类的时候,如果类满足了__get____set____del__这三个方法就可以称之为描述符。

描述符的两个目的:
- 代码重用
- 描述符属性必须定义成类属性

    class Property(object):
        def __init__(self, propname, datatype, default = None):
            self._name ='_' + propname + '_'
            #这个在最终在实例中的属性是什么名字
            self._type = datatype
            self._default = default if default else self._type()
     
        def __get__(self, instance, owner):
            return getattr(instance, self._name, self._default)
            #getattr是python内置的获取属性的值,如果没有是缺省值
            pass
        def __set__(self, instance, value):
            if not isinstance(value, self._type):
                raise TypeError('TypeError, must be %s type0' self._type)
            #isinstance()是判断是不是一个类型的,raise是抛出异常
            setattr(instance, self._name, value)
            #只有这个执行之后,才会真的在实例中创建属性
            pass
        def __del__(self):
            pass

    class Email(Property):
        def __init__(self, propname, default = None):
            pass
        def __set__(self, instance, value):
            pass

    class Chinese(object):
        ID = Property('id', int)
        Name = Property('name', str)
        Email = Email('email')

        def __init__(self, id, name, email):
            self.ID = id
            self.Name = name
            self.Email = email

    VDeamoV = Chinese() #这时候创建Chinese类的时候没有任何属性
    VDeamoV.ID = 123
    VDeamoV.Name = 'test'
    VDeamoV.Email = 'asd@bupt.edu.cn'

类的方法

1. 实例方法
- 第一个参数是self
2. 类方法
  • @classmethod
  • 第一个参数是cls,绑定到类
    这个cls也是它和静态方法的区别,它可以直接用cls来调用自己的类本身而不是向静态方法一样将返回的类写死,很大的区别会体现在继承类的时候返回的类的类型,见下方例子的注释说明

注意:
Python不支持多态,即不支持函数名字相同但是变量不同,但是Cpp支持。

3. 静态方法
  • @staticmethod
  • 和普通函数一样,无绑定
4. 特殊方法
  • 一些内置的如__init__这些函数。
class Test():
    def Hi(self):
        pass
    #实例方法
    #中间略
    @staticmethod
    def getPeopleByParents(father, mother):
        print(father, mother)
        return Chinese(10,'father'+'_son','mother'+'_son')
    #静态方法

    @classmethod
    def getPeopleBySibling(cls, sibling):
        print sibling
        return cls(10, 'TestName', 'blabla')
    #类方法

class Test2(Test):
    pass

a = Test2.getPeopleByParents('blabla', 'blablabla')
b = Test2.getPeopleBySibling('blabls')
#这时候a类的类型为Chinese,而b类的类型为Test2
    

运算符重载

1. 构造函数、析构函数
  • __new__:和__init__的区别在它是在创建内存空间的时候调用
  • __init__:是在初始化的时候才会调用
  • __del__:删除或者回收的时候调用的函数
2. 四则运算
  • __add__
  • __sub__
  • __mul__
  • __div__
3. 比较
  • __lt__
  • __gt__
  • __cmp__
4. 其他
  • __str__
  • __repr__
  • __contains__
  • __contains__
  • __bool__
class Test():
    def __str__(self):
        return "Here is the Return"
    def __repr__(self):
        return "ReprTest"
    def __add__(self, other):
        return self.ID + other.ID
    def __lt__(self, other):
        return self.ID < other.ID

a = Test()
print(a) #得到的结果是"Here is the Return"这个函数
a
#直接敲入对象名称的时候输出的内容由repr来决定

继承

定义:根据已有的类生成新的类
目的:代码重用、多态

class A(object):
    def __init__(self):
        print("In class A")
    pass

class B(A):
    pass

b = B() 
#这个在构造类B的时候继承了类A,所以会执行类A构造的时候的__init__
#不过如果类B自身有__init__的话,就相当于对类A中的__init__进行了重载
#针对第二行注释说的情况如果我们还想使用A的__init__可以用A.__init__的方式
1. 菱形继承

多重继承中出现的问题,当出现菱形的多重继承的时候,函数名解析的问题

  • 经典类:深度优先
  • 新式类:广度优先
    class A(object):
        def __init__(self):
            print("In class A")
        pass
    
    class B(A):
        def __init__(self):
            A.__init__(self)
            print("In B")
    
    class C(A):
        def __init__(self):
            A.__init__(self)
            print("In C")
    
    class D(B,C):
        def __init__(self):
            B.__init__(self)
            C.__init__(self)
            print("In D")
        pass
    
    d = D() 
    #由于继承的时候,有两个相同名字的函数,到底会继承哪个
    #在执行的时候,我们会发现这么调用父函数的话,A类会被执行2遍
    #这样子的话可能会出现问题
    
    解决这种情况的方案就是使用super
    class A(object):
            def __init__(self):
                print("In class A")
            pass
        
    class B(A):
        def __init__(self):
            #A.__init__(self)
            super(B,self).__init__()
            print("In B")
        
    class C(A):
        def __init__(self):
            #A.__init__(self)
            super(C,self).__init__()
            print("In C")
        
    class D(B,C):
        def __init__(self):
            #B.__init__(self)
            #C.__init__(self)
            super(D,self).__init__()
            print("In D")
        pass
    
    调用父类的函数的时候,推荐都适用super调用,这样子不容易出现问题。但是super只支持新式类
2. inspect.getmro(class)
在1中我们知道了,同名函数继承和调用的时候是有一个查找顺序的,除了死记住一个固有的顺序,我们还可以用`inspect`包中的`getmtro`函数。
```python
import inspect
inspect.getmro(D)
```

多线程和多进程

线程和进程的定义

可以参考王道考研操作系统的线程和进程的定义。

1. 进程具有独立功能的程序

进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度的基本单位,它是比进程更小的能独立运行的基本单位。

2. 进程拥有独立的地址空间

一个进程崩溃后,不会对其他进程产生影响。线程没有单独的地址空间,一个线程死掉等于整个进程死掉。所以多进程的程序比多线程的程序鲁棒性要好。

3. GIL和多线程
  • Global Interpreter Lock
    • CPU密集型
      因为是假的多线程,所以CPU其实没有启动多个CPU去运行这些线程,所以效果不是很好。
    • IO密集型
      虽然在CPU分配的方面,线程的优势其实是没有得到很好的效果,但是进程的概念中,还是解决了IO交互速度和CPU运算速度不匹配的问题。所以在这种情况下使用多线程还是有意义的。

多线程模块

1. thread模块:便底层
2. threading模块:

相对高层,提供RLock, Lock等同步机制。

threading的实现细节

继承threading.Thread,实现run()方法。
  • join():挂起当前线程,直到被调用线程结束
  • start():启动线程,执行run中的代码
  • is.Alive():判断线程是否已经结束。
线程中的锁
  • threading.Lock
  • threading.RLock
  • acquire():申请锁
  • release():释放锁
import threading
class A(threading.Thread):
    def __init__(self, n):
        threading.Thread.__init__(self)
        #一定要引入这个父类的初始化
        self._n = n
    def run(self):
        while True:
            print('in thread %s' % self._n)
            time.sleep(1)
    pass

if __name__ == '__main__':
    mt = [A(i) for i in range(4)]
    for t in mt:
        t.start()
    for t in mt:
        t.join()

#运行结果会出现一行多个'in thread',
#原因是,多个线程打印的结果被同时扔进了输出缓存区,
#这样可以看出print操作其实不是一个原子操作

CPU密集的实例

from faker import Factory
faker =. Factroy.create()
cnt = 10000
x1 = [faker.paragraph() for i in range(cnt)]
x2 = [faker.paragraph() for i in range(cnt)]

import time

start = time.clock()
for one in x1:
    len(one)
for one in x2:
    len(one)
end = time.clock()
print('time: %s' % (end - start))

import threading
class A(threading.Thread):
    def __init__(self, x):
        threading.Thread.__init__(self)
        self.x = x
    def run(self):
        for each in self.x:
            len(each)

t1 = A(x1)
t2 = A(x2)

start1 = time.clock()
t1.start()
t2.start()
t1.join()
t2.join()
##等t1,t2都完成了才记录
end1 = time.clock()
print("Thread time: %s" % (end1 - start1))
随着数据的加大,(CPU密集操作的)单线线程的版本反而显著优于多线程

多进程

  1. 多个python进程 = 多个GIL锁
  2. multiprocessing模块
    在python中,多进程的使用是可以充分利用多核CPU,但是进程的切换的开销往往是非常大的。

multiprocessing.Process的细节实现

  1. join():挂起当前进程,直到被调用进程结束。
  2. start():启动进程,执行run中的代码

进程间的通信

  1. 进程的内存空间是独立的,所以是没有锁的概念。
  2. 通过multiprocessing.Queue实现通信
    • Queue.put()
    • Queue.get()
import multiprocessing
class A(multiprocessing.Process):
    def __init__(self, n):
        multiprocessing.Process.__init__(self)
        #一定要引入这个父类的初始化
        self._n = n
    def run(self):
        while True:
            print('in processing %s' % self._n)
            time.sleep(1)
    pass

if __name__ == '__main__':
    mt = [A(i) for i in range(4)]
    for t in mt:
        t.start()
    for t in mt:
        t.join()

#这个时候在进程里面看,会发现出现了5个进程

进程的通信案例,生产者和消费者

import multiprocessing
import time
class Producer(multiprocessing.Process):
    def __init__(self, q):
        multiprocessing.Process.__init__(self)
        self._q = q
    def run(self):
        while True:
            self._q.put('Time is %s' % time.time())
    pass

clas Consumer(multiprocessingl.Process):
    def __init__(self, q, n):
        multiprocessing.Process.__init__(self)
        self._q = q
        self._n = n
    def run(self):
        while True:
            msf = None
            try:
                msg = self._q.get()
            except:
                time.sleep(1)
                continue
            if msg:
                print('in consumer %s' % (self._n,msg))
    pass

if __name__ == '__main__':
    q = multiprocessing.Queue()
    producer = Producer(q)
    c1 = Consumer(q, 1)
    c2 = Cousumer(q, 2)
    producer.start()
    c1.start()
    c2.start()

结语

此次的Python个人总结,由于Python的教程实在非常多,尽量省去了那些耳熟能详的特别容易就能学会的内容,如循环和创建函数类等等。本文挑选的为就目前个人而言,觉得对于初学者比较不常用且重要的内容进行记录。内容的整理来自于目前自身报名的网课“北风网”的课程学习,不过目前不是很推荐大家去学。
最后,文章内容有点杂乱,希望能得到大家谅解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355