Python入门:类与面向对象编程

面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

面向对象编程 VS 面向过程编程:

  • 面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
  • 面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

假设我们要表示学生的成绩(包含姓名+分数),然后处理学生的成绩,举例说明两种程序设计的不同:

面向过程的程序设计

为了表示学生的成绩,面向过程的程序可以用一个dict表示:

std1 = { 'name': 'Michael', 'score': 98 }
std2 = { 'name': 'Bob', 'score': 81 }

处理学生的成绩可以通过函数实现,比如打印学生的成绩:

def print_score(std):
    print('%s: %s' % (std['name'], std['score']))

面向对象的程序设计

如果采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,而是Student这种数据类型应该被视为一个对象,这个对象拥有name和score这两个属性(Property)。如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个print_score消息,让对象自己把自己的数据打印出来。

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def print_score(self):
        print('%s: %s' % (self.name, self.score))

给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method)。面向对象的程序写出来就像这样:

bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.print_score()
lisa.print_score()

面向对象的设计思想是从自然界中来的
在自然界中,类(Class)和实例(Instance)的概念是很自然的
Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念
实例(Instance)则是一个个具体的Student,比如,Bart Simpson和Lisa Simpson是两个具体的Student
所以,面向对象的设计思想是抽象出Class,根据Class创建Instance
面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法
面向对象的三大特点:数据封装、继承和多形态

11.1 类和实例

面向对象最重要的概念就是类(Class)和实例(Instance)

  • 类是抽象的模板,比如Myclass类
  • 实例是根据类创建出来的一个个具体的“对象”
  • 每个对象都拥有相同的方法,但各自的数据可能不同。

class: 创建类Myclass,一般建议将类的首字母大写:

class Myclass:
  pass

为类创建实例my_thing,类似调用函数。类的实例也可以称作为对象

class Myclass:
  pass
my_thing = Myclass()

通过定义类及创建对象的编程模式被称为“面向对象编程”
类可以定义数据类型,决定如何使用该数据类型

type():得到一个数据的类型

print(type({"1":123}))
print(type([1,2,3]))
print(type(123))

# 输出:<class 'dict'>
# 输出:<class 'list'>
# 输出:<class 'int'>

用 type() 来得到一个对象所在的类:

class Myclass:
  pass
my_thing = Myclass()
print(type(my_thing))
#输出: <class '__main__.Myclass'>
  • my_thing:对象
  • Myclass:类
  • __main :正在运行的文件

通过 <class 'main.Myclass'> 知道, Myclass 是正在运行文件中所定义的类

11.2 方法

方法实质上是类中定义的函数,它至少有一个参数,其中第一个参数总是该调用方法的对象,习惯上我们将第一个参数命名为 self

class Myphone:
  def ring(self):
    print("叮铃铃铃……")
phone = Myphone()
phone.ring()
# 输出:叮铃铃铃……

示例中,类 Myphone 中定义了一个方法 ring()
phone 为 Myphone 的一个对象
当 phone 调用方法 ring() 时,程序输出 "叮铃铃铃……"

方法的参数

除了参数 self 之外,类的方法还可以加入更多的参数

class Myphone:
  price = 4000
  def cal(self,discount):
    return self.price * discount
phone = Myphone()
print(phone.cal(0.8))
# 输出:3200.0

方法 cal() 中除了 self 之外,还有一个 discount 参数

cal() 方法返回 self.price 与 discount 的乘积

当调用cal() 时,self 参数不用赋值,此处为 discount 赋值为 0.8,运行结果输出 3200.0

11.3 变量和属性

类的变量

在类中创建类的变量,类的变量对类的对象都一致
通过 对象名.变量名的方式来获取变量的值

class Myclass:
  name = "智能手机"
my_thing = Myclass()
print(my_thing.name)
# 输出:智能手机

类 Myclass ,变量 name,赋值为 "智能手机"
my_thing 是类Myclass的一个对象,my_thing.name 的值为 "智能手机"。

11.4 属性

类的变量与对象的变量都是对象的属性。如果我们调用的对象属性不存在——既不是类变量,也不是对象变量,计算机会返回属性错误:AttributeError。

# .get() 是字典类数据的属性,字符串数据不具备该属性
print({"a":"apple"}.get("a"))
# 输出: apple
print("apple".get("apple"))
# 返回 AttributeError: 'str' object has no attribute 'get'
# 字符串没有 get 属性

对象的变量

我们之前学习了类的变量,以及如何为类创建属性。但有时,属于类的不同对象会有不同的属性值。比如同样是宠物的名字,有的宠物叫“阿黄”,有的宠物叫“小咪”。我们可以通过为对象的变量赋值来操纵对象的具体属性。请见示例:

class Phone:
    pass
hua_wei = Phone()
xiao_mi = Phone()
hua_wei.name = "华为"
hua_wei.color = "黑色"
xiao_mi.name = "小米"
xiao_mi.color = "白色"
print("这部" +hua_wei.name + "是" + hua_wei.color) #输出:这部华为是黑色
print("这部" +xiao_mi.name + "是" + xiao_mi.color) #输出:这部小米是白色

示例中,我们通过 hua_wei.name,hua_wei.color分别为对象 hua_wei 的变量 name,color (属性)赋值;类似地,通过 xiao_mi.name,xiao_mi.color 分别为 xiao_mi 的变量 name,color (属性)赋值。

查看属性

我们可以用dir()来看看某个对象具有哪些属性。

a = "apple"
print(dir(a))
# 输出:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

上方示例中,有一些字符串属性我们在之前的课程学习过。除了字符串,我们还可以用dir()得到诸如列表、字典等其他数据类型的属性:

print(dir({}))
# 输出:
['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

上方示例中,我们用dir()导出了字典数据的属性。

class Phone:
    def __init__(self,name):
        self.name =name

    def number(self,num):
        self.num = num
        return "这部" + self.name + "手机的号码是:" + self.num
hua_wei = Phone("华为")
hua_wei.price = 5000
xiao_mi = Phone("小米")

print(dir(hua_wei))
# 输出:['__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'name', 'number', 'price']
print(dir(xiao_mi))
# 输出:['__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__repr__', '__setattr__', '__str__', 'name', 'number']

建立初始属性

在创建类的时候,可以用 __init__()为类建立初始属性
每当建立类的对象时,就可以为对象的属性赋值
我们来看之前的方法,以做比较:

class Phone:
    name = "手机"
    color = "白色"
my_phone = Phone()
your_phone = Phone()

print(my_phone.name) # 输出:手机
print(my_phone.color) # 输出:白色
print(your_phone.name) # 输出:手机
print(your_phone.color) # 输出:白色

示例中,当我们创建对象 my_phone 和 your_phone时,都接收了类 Phone 的 name,color 属性:手机和白色。而下方代码:

class Phone:
  def __init__(self,name,color):
    self.name = name
    self.color = color
hua_wei = Phone("华为","黑色")
xiao_mi = Phone("小米","白色")
print(hua_wei.name) # 华为
print(hua_wei.color) # 黑色
print(xiao_mi.name) # 小米
print(xiao_mi.color) # 白色

示例中,我们用了__init__()方法在创建类 Phone 时,就设置了初始属性与对象 self 之间的关系。每当建立新的对象,比如 hua_wei,xiao_mi 时,可以为它们各自的 name,color 属性附上不同的值。

举例:
家里又新添了一个成员,猫小咪。我们来看看阿黄和小咪今年都多大了:
1.创建类 Pet;
2.创建init(),参数 self, name, age;
3.在init()内,令参数 name 为 self.name 赋值;令参数 age 为 self.age 赋值;
4.创建Pet的对象a_huang,name 为 "阿黄",age 为 3;
5.创建Pet的对象xiao_mi,name 为 "小咪",age 为 2;

class Pet:
  def __init__(self,name,age):
    self.name = name
    self.age = age
a_huang = Pet("阿黄",3)
xiao_mi = Pet("小咪",2)
print("{}今年{}岁了".format(a_huang.name,a_huang.age))
print("{}今年{}岁了".format(xiao_mi.name,xiao_mi.age))

关键字self

之前我们学到的__init__()方法中的关键字 self代指对象自身,我们还可以见到它更强大的用法:

class Phone:
    def __init__(self,name):
        self.name =name
    def number(self,num):
        self.num = num
        return "这部" + self.name + "手机的号码是:" + self.num
hua_wei = Phone("华为")
xiao_mi = Phone("小米")
print(hua_wei.number("13813813818"))
print(xiao_mi.number("13913913919"))
#输出:这部华为手机的号码是:13813813818
#输出:这部小米手机的号码是:13913913919

上方示例中,在类 Phone 中,__init__()方法中变量为 name;方法 number() 中变量为 num,令self.num 等于 num。

因此,属于类 Phone 的对象 hua_wei,xiao_mi 也具有方法 number(),输出 hua_wei.number("13813813818") 时,屏幕显示:"这部华为手机的号码是:13813813818"

11.5 继承和多形态

假设我们有一个建好的类 A,此时我们想新建一个类 B,类B 中有些属性和类A相同,为了降低代码的重复性,我们可以让类B继承类A的部分属性。

class Product:
  name = "华为"

class Laptop(Product):
  pass

print(Laptop.name)
#输出:华为

上方示例中,类Laptop后有一个() ,括号内填入的参数是类 Laptop 继承的类:Product。类Laptop 继承了类Product的name属性:"华为"。

我们将被继承的类称为父类,继承的类称为子类。

改写父类

当子类继承父类时,难免会出现部分属性或方法与父类相冲突的情况。这时,我们需要在子类对继承来的属性或方法做改写。

class Product:
    def __init__(self,name):
        self.name =name

    def number(self,num):
        self.num = num
        return self.name + "的号码是:" + self.num

class Phone(Product):
    pass
    
class Laptop(Product):
    def number(self):
        return "笔记本没有号码"

huawei_phone = Phone("华为手机")
huawei_laptop = Laptop("华为笔记本")
print(huawei_phone.number("13813813818"))
# 输出:华为手机的号码是:13813813818
print(huawei_laptop.number())
# 输出:笔记本没有号码

上方示例中,类Phone和Laptop都是类Product的子类,但是由于笔记本没有号码,因此在类Laptop中对number() 的内部代码进行了重新改写:去掉了参数 num,修改了返回的字符串。

class Pig:    
    def __init__(self):
      self.eye = 2
      self.foot = 4
      self.tail = 1
    def sing(self):
        # 猪可不会唱歌哦
        return False
    def speak(self):
        # 猪也不会说话!
        return False

# Peppa 继承 Pig
class Peppa(Pig):
    # 佩奇会唱歌
    def sing(self):
        return "啦啦啦"
    # 佩奇会说话    
    def speak(self):
        return "我是佩奇,这是我的弟弟乔治"

peppa_pig = Peppa()

print("佩奇有{}只眼睛".format(peppa_pig.eye))
print("佩奇有{}只蹄子".format(peppa_pig.foot))
print("佩奇有{}条尾巴".format(peppa_pig.tail))
print("佩奇唱:"+peppa_pig.sing())
print("佩奇说:"+peppa_pig.speak())

# 输出:佩奇有2只眼睛
# 输出:佩奇有4只蹄子
# 输出:佩奇有1条尾巴
# 输出:佩奇唱:啦啦啦
# 输出:佩奇说:我是佩奇,这是我的弟弟乔治

多形态

多形态:同一个运算符号在不同数据间体现不同运算方法的现象
比如运算符 + 在不同的数据类型之间有不同的运算功能:

1 + 2 == 3
"你" + "好" == "你好"

当数据类型都是 int 时,+ 的功能是做求和运算
当数据类型都是 str 时,+ 将前后两者联结

11.6 神奇方法

当我们新创建一个对象,直接输出该对象时,默认返回的是该对象的内存地址:

class Phone:
    def __init__(self,name):
        self.name =name    
hua_wei = Phone("华为")
print(hua_wei.name)
# 输出:华为
print(hua_wei)
# 输出: <__main__.Phone object at 0x106ebdb70>
# 以上为hua_wei的内存地址

对象的内存地址有时对我们用处并不大,如果在输出对象时,我们想看到特定信息。可以用 __repr__()方法实现:

class Phone:
  def __init__(self,name):
        self.name =name    
  def __repr__(self):
    return "这是一部" + self.name +"手机"
hua_wei = Phone("华为")
print(hua_wei)
# 输出:这是一部华为手机

上方示例中,我们在类 Phone 中加了一个 __repr() __方法

当输出对象时,返回 "这是一部" + self.name +"手机",其中 self.name是对象的 name 属性,该例子里是 "华为"。

__repr() __只可以有一个参数self,而且返回的必须是字符串。

class Famous:
  def __init__(self,name):
    self.name = name

  def __repr__(self):
    # 下方判断 self.name 是否为 "鲁迅"
    if self.name == "鲁迅":
      return "{}:在我的后园,可以看见墙外有两株树。".format(self.name)
    # 下方判断 self.name 是否为 "海明威"
    if self.name == "海明威":
      return "{}:世界是个美好的地方,值得为它奋斗。".format(self.name)
    if self.name == "维特根斯坦":
      return "{}:语言的边界就是世界的边界。".format(self.name)

lu_xun = Famous("鲁迅")
# 下方创建对象,name 属性为 "海明威" 
hemingway = Famous("海明威")
wittgenstein = Famous("维特根斯坦")
print(lu_xun)
print(hemingway)
print(wittgenstein)
# 输出: 鲁迅:在我的后园,可以看见墙外有两株树。
# 输出: 海明威:世界是个美好的地方,值得为它奋斗。
# 输出: 维特根斯坦:语言的边界就是世界的边界。

我们之前学过__init()____repr()__两种方法,像类似这种名字前后带有两个英文下划线的方法在 Python 中被称为神奇方法。之前我们学到的__init()____repr()__ 只是众多神奇方法中的冰山一角

class Clock: 
  def __init__(self, time):
    self.time = time
    
  def __repr__(self):
    return "{time}点".format(time = str(self.time))
    
  def __add__(self, hour):
    self.hour = hour
    new_time = (self.time + hour) % 24

    return Clock(new_time)

# 假设现在是23点
now = Clock(23)
print(now + 20) # 输出:19点
print(now + 1) # 输出:0点

上方示例中,我们用__add__()方法重新定义 + 的运算规则:self.time 与 hour 的和除以 24 的余数。所以当变量 now 定为 23点,now + 20 的运算结果为 19; now + 1 的运算结果为 0。

class StudentGroup:
  def __init__(self, students, status):
    self.student_list = students
    self.status = status

  def __iter__(self):
    return iter(self.student_list)

  def __len__(self):
    return len(self.student_list)

  def __contains__(self, student):
    return student in self.student_list

上方示例中,我们分别采用了 __init()__iter()__len()__contains()四个神奇方法:
(1)__init__()之前学过,可以用来设置一个对象变量的初始值,在这里,我们设置了 self, students 和 status 三个参数;
(2)__iter__()中的 iter 是英文单词 iterate(重复) 的含义,用了__iter() 方法可以将 for student in students 这样的语句用在 self.student_list 上;
(3)__len__()可以在调用 len(student_group) 时,返回列表 student_group的长度;
(4)__contains__()可以判断元素 student 是否在 self.student_list 之中。

在此基础之上,我们在下方代码继续创建类 Student,及其对象 a_qiang,lao_wang和 xiao_ming;接着创建类StudentGroup 的对象 completed 与 not_completed,并且用上述神奇方法操纵它们:

class Student:
  def __init__(self, name):
    self.name = name

a_qiang = Student('阿强')
lao_wang = Student('老王')
xiao_ming = Student('小明')

# 将 a_qiang、lao_wang 和 xiao_ming 分别放入 completed 与 not_completed 之中
completed = StudentGroup([a_qiang, lao_wang], {'任务完成': True})
not_completed = StudentGroup([xiao_ming], {'任务完成': False})

print(len(completed))
# 输出: 2

for student in completed:
  print(student.name)
# 输出:
# "阿强"  
# "老王"

if xiao_ming in not_completed:
  print("{name} 没有完成任务".format(name = xiao_ming.name))
# 输出:小明 没有完成任务

上方示例中,我们创建了类 Student,并以此创建了 三个对象 a_qiang,lao_wang 和 xiao_ming。我们将它们分别放进不同的StudentGroup中:completed 和 not_completed。
1.小明完成了学习任务,请将 xiao_ming 从 not_completed 中移出至 completed 中;
2.妞妞刚报名了课程,还未完成任务,请创建类Student的对象 niu_niu,name 为 "妞妞";
3.将niu_niu 添加至 not_completed。

class StudentGroup:
  def __init__(self, students, status):
    self.student_list = students
    self.status = status

  def __iter__(self):
    return iter(self.student_list)

  def __len__(self):
    return len(self.student_list)

  def __contains__(self, student):
    return student in self.student_list


class Student:
  def __init__(self, name):
    self.name = name

a_qiang = Student('阿强')
lao_wang = Student('老王')
xiao_ming = Student('小明')
niu_niu = Student("妞妞")

completed = StudentGroup([a_qiang, lao_wang, xiao_ming], {'任务完成': True})
not_completed = StudentGroup([niu_niu], {'任务完成': False})

print("完成的同学有:")
for student in completed:
  print(student.name)

print("未完成的同学有:")
for student in not_completed:
  print(student.name)


# 输出:完成的同学有:
# 输出:阿强
# 输出:老王
# 输出:小明
# 输出:未完成的同学有:
# 输出:妞妞

分别输出完成和未完成任务的学生人数

class StudentGroup:
  def __init__(self, students, status):
    self.student_list = students
    self.status = status

  def __iter__(self):
    return iter(self.student_list)

  def __len__(self):
    return len(self.student_list)

  def __contains__(self, student):
    return student in self.student_list


class Student:
  def __init__(self, name):
    self.name = name

a_qiang = Student('阿强')
lao_wang = Student('老王')
xiao_ming = Student('小明')
niu_niu = Student("妞妞")

completed = StudentGroup([a_qiang, lao_wang, xiao_ming], {'任务完成': True})
not_completed = StudentGroup([niu_niu], {'任务完成': False})

print("完成的同学有{}个".format(len(completed)))
print("未完成的同学有{}个".format(len(not_completed)))

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

推荐阅读更多精彩内容