Python 面向对象

面向对象

创建类

通过class关键字定义,类名最好以大写字母开头,举例:

class Test:
    pass
实例化

直接调用类即可实例化,举例:

class Test: pass
t = Test()
定义属性/方法

举例:

class Test:

    # 定义属性a
    a = 1
    
    # 定义构造方法
    def __init__(self, b):
        # 定义对象属性b
        self.b = b

    # 定义对象方法m(后面会介绍几种方法的定义)
    def m(self, v):
        print(self.a, self.b, v)
调用对象属性和方法

通过.来调用,举例:

class Test:
    
    # 定义属性a
    a = 1
    
    # 定义构造方法
    def __init__(self, b):
        # 定义对象属性b
        self.b = b

    # 定义方法m
    def m(self, v):
        print(self.a, self.b, v)
        
t = Test(2)
t.m(3)

# 1 2 3
类属性/对象属性

在定义类时定义的属性被称为类属性,在实例化的对象里定义的属性为对象属性,简单来说对象在调用属性时,会优先调用对象属性,如果该属性不存在,才会去类中查找该属性,并调用(实际上调用属性顺序远不止这么简单,后面会介绍),举例:

class A:
    # 定义类属性a
    a = 0

a = A()
print(a.a)  # 0,对象属性a不存在,读取的是类属性a
a.a = 10    # 设置对象属性a
print(a.a)  # 10,读取的是对象属性
print(A.a)  # 0,类属性没有被修改
A.a = 100   # 修改类属性
print(A.a)  # 100
print(a.a)  # 10,读取的是对象属性
del a.a     # 删除对象属性
print(a.a)  # 100,对象属性被删除,读取的是类属性

注:
上面说的是简单的情况,实际的调用顺序后面会介绍,并且之后会有相关的魔法方法,如果自定义了相关魔法方法,情况和默认的又不一样了

类继承

在类名的括号里加上父类名,且python支持多继承关系,举例:

class A: pass
class B: pass
# 继承自B类
class C(B): pass
# 继承自C和A类
class D(C, A): pass

多继承的调用关系参照后面介绍的mro顺序,一般来说就是在非菱形继承(多个父类继承自另一个相同的父类)的情况下基于深度优先,在菱形继承的情况下基于广度优先

所有类的基类

所有类都继承于object类(除了object类,因此,object类是唯一父类为空的类),可以通过__bases__属性查看一个类的所有基类(直接父类),举例:

class A: pass

print(A.__bases__)
print(object.__bases__)

# (<class 'object'>,)
# ()
重写

子类中可以通过重写定义同名方法来重写父类方法,如果希望调用父类方法,可以通过super()获取父类(单继承的情况下super()指向父类,多继承就未必,后面会介绍),然后调用,举例:

class A:
    def test(self):
        print(1)
        
class B(A):
    # 重写父类A的test方法
    def test(self):
        # 调用父类A的test方法
        super().test()
        print(2)
        
b = B()
b.test()

# 1
# 2
私有属性

默认定义的属性和方法都是公有的,如果希望改成私有的,可以就在前面加上双下划线__,举例:

class A:
    __a = 1
        
a = A()
print(A.__a)
print(a.__a)

# 上面两个print都会报错:不存在__a属性

但实际上python中没有真正将属性变为私有的方法,上面的实质并不是将属性变为私有,而是将属性重命名成:_类名__属性,举例:

class A:
    __a = 1
        
a = A()
print(A._A__a)
print(a._A__a)

# 1
# 1

可以看出该方式只是将对外暴露的属性名给重命名了,通过新的命名方式,还是可以在外部调用该属性。因此在python里并没有真正的私有属性,不过该方式能够很好的解决继承类的属性名冲突问题,例如:

class A:
    a = 1
class B(A):
    a = 2

a = B()
print(a.a)  # 2

此时在B的实例化对象里就无法获取A中的a属性,但是如果使用私有属性,因为属性名会被修改成以类名开头的格式,因此不会导致属性名冲突,举例:

class A:
    __a = 1
class B(A):
    __a = 2

a = B()
print(a._A__a, a._B__a) # 1 2
实例化相关方法
__new__

在对象生成之前执行,接收当前类作为参数,可以自定义类的生成过程,举例:

class A:
    def __new__(cls):
        print("创建对象")
        obj = super().__new__(cls)
        obj.a = 1
        return obj
        
a = A()
print(a.a)

# 创建对象
# 1

可以看出在创建对象前执行,并且可以进行一些类相关的操作,如示例中给对象添加了属性值。由于__new__返回处理后的类,然后根据返回的类生成对象,所以假如__new__里不返回实例化的对象,那么后面的方法如__init__也就不会执行了

__init__

在对象生成之后执行,接收当前对象作为参数,可以进行一些初始化操作,完善对象,举例:

class A:
    def __init__(self, a):
        print("初始化对象")
        self.a = a
        
a = A(1)
print(a.a)

# 初始化对象
# 1

__init__返回必须返回None对象,否则会抛出TypeError异常

python中的对象特征
  • 身份:内存地址,可以通过id函数查看,代表其唯一的身份
  • 类型

例如a=1,说明变量a指向了一个地址,这个地址的数据是int类型,值为1

方法

python中支持定义对象方法、类方法、静态方法,以及借助其他模块定义抽象方法

对象方法

对象方法会将对象本身传入第一个参数,因此方法的第一个参数需要接收一个对象,举例:

class A:
    # 定义对象方法,self代表对象本身
    def o_m(self, x):
        print(self, x)

a = A()
# 调用对象方法,会自动将a对象传入第一个参数,等价于:A.o_m(a, 1)
a.o_m(1)

# <__main__.A object at 0x000001BCD5A396D8> 1
静态方法

通过类调用,静态方法不会主动接收类和对象,可以理解成类中定义的函数,通过@staticmethod语法糖装饰的方法即为静态方法,举例:

class A:
    # 定义静态方法,staticmethod不加也能以静态方式调用
    @staticmethod
    def s_m(x):
        print(x)

# 调用静态方法
A.s_m(1)
A().s_m(1)

# 1
# 1

注:
对象方法中能够使用到各种实例属性和类属性,但前提是必须要先实例化才能调用,而有些方法希望不用实例化也可以调用,此时就可以使用静态方法。静态方法可以理解为在某个类命名空间下定义的函数
注2:
如果静态方法不加上staticmethod装饰器,那么该方法是静态方法还是对象方法将由调用方式来决定,举例:

class A:
    def t_m(x):
        print(x)


A.t_m(111)
# 通过类调用则表现为静态方法
A().t_m()
# 通过对象调用则表现为对象方法

# 111
# <__main__.A object at 0x0000024EEC3DA6A0>

所以如果以类方式去调用静态方法,则无需加staticmethod装饰器。但如果加上staticmethod装饰器,那么无论是类调用还是对象调用,都表现为静态方法,举例:

class A:
    @staticmethod
    def t_m(x):
        print(x)


A.t_m(111)
# 通过类调用表现为静态方法
A().t_m(111)
# 通过对象调用也表现为静态方法

# 111
# 111
类方法

类方法会将类本身传入第一个参数,通过@classmethod语法糖装饰的方法即为类方法,举例:

class A:
    # 定义类方法,第一个参数为类本身
    @classmethod
    def c_m(cls, x):
        print(cls, x)

# 调用类方法,等价于:A.c_m(A, 1)
A.c_m(1)

# <class '__main__.A'> 1

注:
在静态方法或者对象方法当中假如要使用到类属性或者类方法,那么就只能通过类.xxx来进行调用,而这样会带来一个问题——当类名修改了以后,静态方法里也得跟着修改,而类方法就能解决这个问题

抽象方法

抽象方法只负责定义方法,而不负责实现,继承的父类中如果含有抽象方法,则子类必须实现该方法,常见的定义抽象方法有两种方式:抽象方法中抛异常;借助元类定义

抽象方法中抛异常

可以在定义的抽象方法当中抛出未实现的异常,若子类没有实现该方法,那么在调用该方法时就会报错,举例:

class A():
    # 定义抽象方法,未实现则无法调用
    def a_m(self):
        raise NotImplementedError("未实现的抽象方法")
    
class B(A):
    def a_m(self):
        print(111)

class C(A): pass

B().a_m()
C().a_m()

# 111
# NotImplementedError: 未实现的抽象方法

可以看出C类没有实现该方法,于是调用时报错。但该方式实现的抽象方法必须在调用时才会报错,如果希望实例化时就报错,那么可以通过第二种方式实现

借助元类定义

通过abc模块下的abstractmethod装饰实现,并且定义抽象方法的类需要设置元类为ABCMeta(元类后面会介绍),举例:

from abc import ABCMeta, abstractmethod

# 定义元类
class A(metaclass=ABCMeta):
    # 定义抽象方法
    @abstractmethod
    def a_m(self):
        pass
    
class B(A):
    def a_m(self):
        print(111)

class C(A): pass

B().a_m()
C()

# 111
# TypeError: Can't instantiate abstract class C with abstract methods a_m

可以看出B类中实现了对应的抽象方法,所以可以正常使用,而C类中没有实现,所以在实例化时就会报错

属性描述符

@property

可以设置对象只读,以及设置修改对象时必须通过对应的setter方法修改,举例:

class Person:
    def __init__(self):
        self._name = "default"
        self._age = 18

    @property
    def name(self):
        return self._name
    
    @name.setter
    # name属性的setter方法
    def name(self, new_name):
        print("修改name对象")
        self._name = new_name

    @property
    # age没有setter方法,为只读属性
    def age(self):
        return self._age

p = Person()
print(p.name, p.age)
p.name = "aaa"
print(p.name, p.age)
# p.age = 20
# age只读,这句会报错

# 结果:
# default 18
# 修改name对象
# aaa 18

参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017502538658208

魔法方法

可以理解为类的生命周期方法(或者说类在各种行为中的表现方式),在实例化对象中执行特定行为时将触发,是python中的类能够功能如此强大的重要特性,魔法方法都以双下划线__开头和结尾,举例:

class A:
    # 定义对象的构造方法
    def __init__(self, *args):
        print("初始化对象")
        self._data = args
    
    # 定义获取对象长度的触发方法
    def __len__(self):
        return len(self._data)
    
    # 定义索引对象内容时的触发方法
    def __getitem__(self, index):
        return self._data[index]
    
    # 定义对象被调用的触发方法
    def __call__(self):
        print("对象被调用")
    
a = A(1,2,3)
print(len(a))
print(a[1])
a()

# 初始化对象
# 3
# 2
# 对象被调用

魔法方法详细参考我的另一篇:Python 魔法方法总结

类相关内置函数

type(obj)

获取当前对象的所属类,举例:

class A: pass

a = A()
print(type(a))

# <class '__main__.A'>

type函数还可以用于创建类(实际上类默认就是通过type来创建的),此时需要传入三个参数,分别是:类名、基类和属性,举例:

def test(self): pass
A = type("A", (), {"x": 1, "y": test})
print(A)

# <class '__main__.A'>

注:
type创建类是元类编程的基础,后面会介绍

issubclass(clsA ,clsB)

判断A类是否为B类的子类,结果返回一个布尔对象,并且第二个参数可以传入一个元组,只要第一个参数类是元组中其中一个类的子类就返回True,举例:

print(issubclass(dict, list))
print(issubclass(dict, (object, list)))

# False
# True
isinstance(obj , cls)

判断一个对象是否为某个类的实例,使用方法和issubclass类似,举例:

print(isinstance({}, list))
print(isinstance({}, (dict, list)))

# False
# True

注:
type/isinstance区别:type是判断是否为同一个类(不包括父类),而isinstance则包括父类的判断,举例:

>>> class A: pass
>>> class B(A): pass
>>> isinstance(B(), B)
True
>>> isinstance(B(), A)
True
# 包括父类也能判断
>>> type(B()) is B
True
>>> type(B()) is A
False
# 不判断父类
hasattr(obj ,attr)

反射相关,判断对象里面是否有指定的属性或方法,举例:

class A:
    a = 1
    def b(self):
        pass
    
print(hasattr(A(), "a"))
print(hasattr(A(), "b"))
print(hasattr(A(), "c"))

# True
# True
# False
getattr(obj ,attr [,default])

反射相关,获取一个对象中指定属性的值,其中可以对不存在的属性设置默认值,否则会抛出AttributeError异常,举例:

class A:
    a = 1
    def b(self):
        pass
    
print(getattr(A(), "a"))
print(getattr(A(), "b"))
print(getattr(A(), "c", "default"))
print(getattr(A(), "c"))

# 1
# <bound method A.b of <__main__.A object at 0x00000274558496D8>>
# default
# AttributeError: 'A' object has no attribute 'c'
setattr(obj ,attr ,value)

反射相关,给对象的指定属性赋值,如果属性不存在则会创建该属性,举例:

class A:
    a = 1
    
a = A()
setattr(a, "a", 2)
print(a.a)
setattr(a, "b", 1)
print(a.b)

# 2
# 1
delattr(obj ,attr)

反射相关,删除对象中指定属性,举例:

class A:
    a = 1
    def __init__(self):
        self.b = 2
    
a = A()
print(hasattr(a, "a"))
# a是类属性,要从类中删除
delattr(A, "a")
print(hasattr(a, "a"))

print(hasattr(a, "b"))
# b是对象属性,要从对象中删除
delattr(a, "b")
print(hasattr(a, "b"))
property(get ,set ,del)

描述符相关,能够定义一个属性的get/set/del操作,举例:

class A:
    def setv(self, v):
        print("设置v的值")
        self._v = v
    def getv(self):
        print("获取v的值")
        return self._v
    def delv(self):
        print("删除v")
        del self._v
    v = property(getv, setv, delv)

a = A()
a.v = 100
print(a.v)
del a.v

# 设置v的值
# 获取v的值
# 100
# 删除v
super(cls, obj)

获取指定类的父类(一般情况下,后面会说明),并将当前对象转成指定类的对象,举例:

class A:
    def __init__(self):
        self.x = 1
    def test(self, y):
        print("A", self.x, y)

class B(A):
    def __init__(self):
        self.x = 2
    def test(self, y):
        super(B, self).test(y)
        # 获取B的父类A,并将当前对象转成A类,从而调用A类的test方法

b = B()
b.test(2)

# A 2 2

可以看出通过super函数,对象b转成A类的对象,但属性x并没有因为转成A类而改变。

super函数并非类中才能使用,举例:

class A:
    def __init__(self):
        self.x = 1
    def test(self, y):
        print("A", self.x, y)

class B(A):
    def __init__(self):
        self.x = 2

super(B, B()).test(100)

# A 2 100

注:
在单继承的情况下super的确可以简单地理解为寻找父类,但实际上其是寻找mro(方法解析顺序,后面会介绍)列表中的指定类的下一个索引类,详细参考:
https://www.jianshu.com/p/de7d38c84443

类对象内置属性

__class__

创建当前对象的类,举例:

class A: pass

a = A()
print(a.__class__, A.__class__)

# <class '__main__.A'> <class 'type'>

可以看到创建类的类默认是type,这个涉及到元类编程,后面会介绍

__name__

当前类的名称,举例:

class A: pass

a = A()
print(A.__name__)

# A
__dict__

python自省机制实现的核心(自省机制:通过一定的机制查询到对象内部的结构),一个类(类在python中本身也是对象)/对象所有的属性是通过__dict__属性(字典对象)进行管理的,举例:

class A:
    a = 1
class B(A):
    def __init__(self):
        self.b = 2

b = B()
print(b.__dict__)  # 类实例对象属性
# {'b': 2}
print(B.__dict__)  # 类对象属性
# {'__module__': '__main__', ...}
print(A.__dict__)
# {'__module__': '__main__', 'a': 1, ...}

可以看出首先b中有实例属性b,B中有类属性一大堆,而属性a则在A类里,当b获取属性a时,先寻找自身的__dict__当中是否存在属性a,不存在则去找B里面的,然后再去找A里面的。

并且我们也可以通过__dict__来对属性进行增删改之类的操作,举例:

class A:
    a = 1
class B(A):
    def __init__(self):
        self.b = 2

b = B()
b.__dict__["c"] = 3
print(b.c)
# 3
print(b.__dict__)
# {'b': 2, 'c': 3}

注:
可以通过dir函数能得到对象能够获取到的所有属性(__dict__只能获取到当前对象的,而通过__mro__查找到的属性无法展示,dir则可以),举例:

class A:
    a = 1
class B(A):
    def __init__(self):
        self.b = 2

b = B()
print(b.__dict__)
# {'b': 2}
print(dir(b))
# ['__class__', '__delattr__', '__dict__', ..., 'a', 'b']

python自省机制参考:https://kb.cnblogs.com/page/87128/

__slots__

限制实例化的对象中允许有哪些属性,在python中是默认允许对象动态添加属性和方法的,比如下面的代码:

class A:
    def __init__(self):
        self.b = 1

A.c = 2
# 类A添加属性c
a = A()
a.d = 3
# 对象a添加属性d
print(a.b, a.c, a.d)

可以看到定义好的类A后来有给添加的属性cd,但这样有可能会给代码带来一些风险,所以有时候我们要限制能够添加的属性,此时可以通过__slots__属性来设置,举例:

class A:
    __slots__ = ['b', 'c']
    # 限制实例化的对象只能有b和c属性
    def __init__(self):
        self.b = 1

a = A()
a.c = 3
# 对象添加属性c
# a.d = 3
# 对象只能有b和c属性,因此无法添加,这句会报错
A.d = 2
# 类A添加属性d,这句是可以的
print(a.b, a.c, a.d)
__bases__

能够查看当前类的所有直接父类,举例:

>>> class A: pass
>>> A.__bases__
(<class 'object'>,)
# 默认是object(python3下不指定基类,默认继承object)
>>> class B(A): pass
>>> B.__bases__
(<class '__main__.A'>,)
>>> class C(B): pass
>>> C.__bases__
(<class '__main__.B'>,)

__bases__只列出直接父类,如果希望查看所有的父类(包括间接的),可以通过__mro__属性查看

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(D.__bases__)
print(D.__mro__)

# (<class '__main__.B'>, <class '__main__.C'>)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

还有__base__属性可以查看其super指向的类,举例:

class A:pass
class B(A):pass
class C(A):pass
class D(B, C):pass

print(D.__bases__)
print(D.__base__)

# (<class '__main__.B'>, <class '__main__.C'>)
# <class '__main__.B'>
__subclasses__

查看该类的所有子类,举例:

class A: pass

class B(A): pass

class C(A): pass

class D(B, C): pass

print(A.__subclasses__())

# [<class '__main__.B'>, <class '__main__.C'>]

可以发现该方法只能查看到该类的直接子类,如果想要查看所有子类,可以封装如下:

class A: pass

class B(A): pass

class C(A): pass

class D(B, C): pass

class E(D): pass

def get_all_subclass(c):
    queue = [c]
    res = []
    while queue:
        c = queue.pop(0)
        sub_cs = c.__subclasses__()
        for sub_c in sub_cs:
            if not sub_c in res:
                queue.append(sub_c)
                res.append(sub_c)
    return res

print(get_all_subclass(A))

# [<class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.E'>]

注:
通过dir(类)会发现__subclasses____base__等属性并不在里面,那为何类能使用这些属性呢?因为所有的类最终都是基于type类(或者说对象)来创建的,我们可以查看type属性如下:

print(dir(type))

# ['__abstractmethods__', '__base__', '__bases__', ..., '__subclasscheck__', '__subclasses__', ...,  'mro']

可以发现这些属性都是存在于type类中的,而所有的类可以认为是type类创建的对象,因此对象里自然能够使用这些方法

__module__

查看类所在模块,举例:

class A: pass

print(A.__module__, type(A.__module__))

# __main__ <class 'str'>

但可以看出该方式获取的是字符串的模块名,如果希望获取模块对象本身,可以通过__import__函数动态导入模块,或者从sys.modules(存放已载入模块的字典对象)中获取已载入的模块,举例:

import sys

class A: pass

# 通过动态导入获取模块
print(__import__(A.__module__))
# 从已载入模块的字典中找寻模块
print(sys.modules.get(A.__module__))

# <module '__main__' from 'xxx:\\test.py'>
# <module '__main__' from 'xxx:\\test.py'>
__annotations__

函数/类标注,指定期望的参数类型以及返回值类型,举例:

class A:
    aaa: int = 1

def test(aaa: str, bbb:int = 1) -> list:
    pass

print(test.__annotations__)
print(A.__annotations__)

# {'aaa': <class 'str'>, 'bbb': <class 'int'>, 'return': <class 'list'>}
# {'a': <class 'int'>, 'return': <class 'str'>}

相关概念

鸭子类型

当一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子

什么意思呢?例如在Python里,我可以实现多个类,只要他们都实现了相同的方法,那么不论换哪个,都能正常使用,举例:

class Bird:
    def walk(self):
        print("Bird walk...")

class Duck:
    def walk(self):
        print("Duck walk...")

duck = None
for animal in [Bird, Duck]:
    duck = animal()
    duck.walk()

这里duck可以变成鸟,也能变成鸭子,并且使用对应的方法,因为没有像静态语言中的类型检查,他不论变成哪个类的实例都没问题,只要实现了需要的方法,就能够被正常调用
而在静态语言里(如Java),鸟就是鸟,鸭子就是鸭子,他们两就算是功能一模一样,也无法进行转换,更无法调用,举例:

public class Test {
    public static void main(String[] args) {
        Duck duck = new Duck();
        duck.walk();
        // duck = new Bird();
        // 这句会报错,因为他们两个类不同,无法直接转换
    }
}

class Bird {
    public void walk() {
        System.out.println("Bird walk...");
    }
}

class Duck {
    public void walk() {
        System.out.println("Duck walk...");
    }
}

所以此时必须要他们继承自同一个类/或者接口,然后使用父类/接口实例化,从而才能转换,举例:

public class Test {
    public static void main(String[] args) {
        Animal duck = new Duck();
        // 使用同一个接口实例化,才能成功转换
        duck.walk();
        duck = new Bird();
        duck.walk();
    }
}

interface Animal {
    public void walk();
}

class Bird implements Animal{
    public void walk() {
        System.out.println("Bird walk...");
    }
}

class Duck implements Animal {
    public void walk() {
        System.out.println("Duck walk...");
    }
}

因此python的多态可以很容易的实现(动态类型的优势),假如我们有多个类,他们实现了相同的方法,那么我们可以很容易的随意修改这个对象指向这几个类,从而调用方法

再举个例子,例如list的extend方法需要传入的是一个可迭代的对象(会隐式调用该对象的迭代器),因此该方法的传入对象不一定非得传入一个list或者什么指定类,而是只要实现了可迭代方法的类都可以,举例:

li = []
s = {1,2,3}
li.extend(s)
# s虽然是集合,但作为可迭代对象,因此可以传入

说白了,python中的一个类,他可以有多重身份,例如实现了迭代器相关的魔法方法,他就可以是一个迭代器,实现了enter和exit,他就可以是一个上下文管理器,而且这些身份可以兼有
而在静态语言当中,基本上就是指定了传入的对象类型


面向对象进阶部分

类的执行机制

和函数的区别

类和函数在定义上有一点很重要的不同就是:函数只有在调用了以后才会开始执行函数里面的语句,而类在调用前就已经执行了初始化定义语句分配好了内存(实际上是编译时会创建类对象,并对类对象进行一些初始化操作),比如下面的定义了一个函数和类:

def a():
    print(1)

class A():
    print(2)
    def __init__(self):
        print(3)

if __name__ == '__main__':
    pass

# 2

可以看出类还未实例化就已经执行了创建类对象的相关语句,即:类已经完成了初始化定义和内存分配

类的执行顺序

不管有没调用类,只要定义了以后,就会默认执行一些初始化语句,所以类在程序中的执行顺序为:类里的最外层语句(初始化定义语句)>__new__>__init__,比如下面的类:

print(0)
class A():
    print(1)
    def __init__(self):
        print(4)
    print(2)

if __name__ == '__main__':
    print(3)
    a = A()

结果为:
0
1
2
3
4

python解释器是逐行执行,所以类外部的执行顺序和类无关,只看执行的语句在哪一行

通过类/对象调用方法的区别

通过类调用方法和对象调用方法的主要区别就是:通过类调用时,相当于直接调用一个函数,而通过对象调用时,会默认第一个参数传入对象本身(类方法则传入类本身),因此obj.method(...)等价于:cls.method(obj, ...),举例:

class A:
    def test(self, x):
        print(self, x)
        
a = A()
a.test(1)
A.test(a, 1)

# <__main__.A object at 0x00000229993F96D8> 1
# <__main__.A object at 0x00000229993F96D8> 1

类继承时的方法解析顺序(MRO)

新式类和经典类

如果一个类或者他的父类继承了object类则是新式类,否则就是经典类,在python3之后都是新式类(新式类如果不设置继承父类,默认继承object类),而对于类继承时的方法解析顺序,在经典类中采用深度优先(因为经典类没有要求必须继承object类,所以可以采用深度优先,像新式类因为必须继承object类,则不适合采用深度优先,否则对于object拥有的方法,多继承的情况下就不太可能轮到别的类上进行查询),而python3中采用C3算法遍历继承的父类关系

C3算法

简单来说:对于有相同父类的继承关系,采用广度优先;无相同父类的继承关系,采用深度优先,例如一个继承关系如下的类:

     object
        |
      /   \
    A      E
  /  \     |
 B    C    |
  \  /     |
    D      F  
     \    /
       X

可以看出BC有相同父类,而DF无相同父类,因此BC之间采用广度优先,BC之间采用深度优先,打印其mro关系,如下:

class A: pass

class B(A): pass

class C(A): pass

class D(B, C): pass

class E: pass

class F(E): pass

class X(D, F): pass

for cls in X.__mro__:
    print(cls)

# <class '__main__.X'>
# <class '__main__.D'>
# <class '__main__.B'>
# <class '__main__.C'>
# <class '__main__.A'>
# <class '__main__.F'>
# <class '__main__.E'>
# <class 'object'>
遍历过程

按照深度优先遍历,如果有相同的父类,则需先遍历完子类再遍历父类,如果遇到重复的父类,则只保留最后一个,因此上面的示例中,计算过程如下:

  1. 首先,对于DF类进行深度优先,因此先遍历D类:X->D
  2. 由于D类的父类当前也是多重继承,并且BC类拥有共同父类A,因此也是先遍历B类:B
  3. 然后在遍历C类,遍历完成即可遍历父类AC->A
  4. 最后遍历F类:F->E->object
  5. 将之前遍历的结果合起来就是:
X->D->B->C->A->F->E->object

参考:
https://www.cnblogs.com/bashaowei/p/8508276.html
https://blog.csdn.net/u011467553/article/details/81437780
https://hanjianwei.com/2013/07/25/python-mro/
http://www.chinaoc.com.cn/p/1196683.html

super调用顺序

前面介绍的super函数实际上就是基于mro顺序,获取下一个类的内容,而并非单纯的找自身父类,举例:

class A:
    def __init__(self):
        print("A")
class B(A):
    def __init__(self):
        print("B")
        super().__init__()
class C(A):
    def __init__(self):
        print("C")
        super().__init__()
class D(B, C):
    def __init__(self):
        print("D")
        super().__init__()

d = D()
print(D.__mro__)
# D
# B
# C
# A
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

可以看出当D的super返回的是B,而B的父类是A,但其super却是C,也就是mro的下一个,最后才是A

None对象

在解释器启动时,会通过None类型生成一个None对象,全局只有一个,举例:

>>> type(None)
<class 'NoneType'>
# 可以看出是NoneType类生成的对象
>>> None is None
True
# 可以看出None对象是唯一的

上面的例子为什么能说明None唯一(很简单,因为对象的id是唯一的,不过如果觉得前面讲的有点绕,可以继续看下面解释):
首先实例化的对象,肯定是互相不同的(因为新分配了空间,id肯定唯一),举例:

>>> class A: pass
>>> id(A())
2396138626464
>>> id(A())
2396138625624
# 可以看出两次实例化的地址不同

None作为NoneType类的对象,必然每次实例化的id也不相同,但这里却相同,所以也就说明了其唯一
这里可以再说一下类,类本身也是对象,但类的id会发现也唯一,举例:

>>> class A: pass
>>> A is A
True

这也可以看出解释器在最开始就会通过type实例化生成各种类对象,并且这些类对象都是唯一的
注:
由于python内部的优化机制,一些简单的整数、字符串等都会先预分配id,所以对于一些简单的数据,可能实例化多个的id是一样的,举例:

>>> id(int(1))
1660742096
>>> id(int(1))
1660742096

None其实也属于被优化的一部分

抽象方法实现原理

这里如果分析源码,可以发现该方式就是首先给绑定了abc.abstractmethod装饰器的方法添加一个__isabstractmethod__属性,并且值为True,源码如下:

def abstractmethod(funcobj):
    funcobj.__isabstractmethod__ = True
    return funcobj

然后当实例化时,通过元类获取当前类以及所有父类的抽象方法名(即获取所有存在__isabstractmethod__属性且值为True的方法,因为通过反射获取方法时,假如子类实现了该方法,那么该方法默认是不存在__isabstractmethod__属性的,假如子类没有实现该方法,那么就会去父类中找该方法,而父类中该方法存在__isabstractmethod__属性,并且值为True),并保存到类的__abstractmethods__属性当中,而解释器则会读取这个属性,并报出对应错误信息,下面是加注释的源码:

def __new__(mcls, name, bases, namespace):
    # 先生成对应的类对象
    cls = super().__new__(mcls, name, bases, namespace)
    # 获取类对象的抽象方法(存在属性__isabstractmethod__且值为True的方法)
    abstracts = {name
                    for name, value in namespace.items()
                    if getattr(value, "__isabstractmethod__", False)}
    for base in bases:
        # 获取所有父类中的抽象方法名
        for name in getattr(base, "__abstractmethods__", set()):
            # 获取当前类对象的该方法
            value = getattr(cls, name, None)
            # 如果当前类的该方法的__isabstractmethod__属性值为True,则说明是从父类中继承来的抽象方法,那么加入到当前类的抽象方法里
            # 如果不为True,说明当前类或者父类里重写了对应的方法,因为方法被重写,因此__isabstractmethod__属性也就不存在了
            if getattr(value, "__isabstractmethod__", False):
                abstracts.add(name)
    # 将当前类的所有抽象方法加入到__abstractmethods__属性当中
    cls.__abstractmethods__ = frozenset(abstracts)
    # 弱引用相关操作...
    cls._abc_registry = WeakSet()
    cls._abc_cache = WeakSet()
    cls._abc_negative_cache = WeakSet()
    cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
    return cls

通过上面的分析,我们可以发现python主要根据属性__abstractmethods__来判断当前类的抽象方法,所以根据抽象方法的实现原理,我们也可以这样实现:

class AnimalBase:
    # 往类属性__abstractmethods__当中指定当前类的抽象方法
    __abstractmethods__ = frozenset({"walk", "eat"})

    def __new__(cls):
        # 定义一个集合存放所有当前类的抽象方法
        abstract = set()
        for base in cls.__mro__:
            # 遍历当前类及父类的所有抽象方法
            for abcmethod in getattr(base, "__abstractmethods__", set()):
                # 如果父类的抽象方法在当前类中没有,则添加到当前类的抽象方法当中
                if not abcmethod in cls.__dict__:
                    abstract.add(abcmethod)
        # 设置当前类的__abstractmethods__属性,注意是当前类,而不是当前类实例化的对象,因为该属性是绑定在类上的
        cls.__abstractmethods__ = frozenset(abstract)
        return super().__new__(cls)

class Duck(AnimalBase):
    pass

duck = Duck()

注:
__abstractmethods__是一个类属性(不是类实例化后对象的属性),用于声明当前类的所有抽象方法

抽象基类

存在抽象方法的类即为抽象基类,抽象基类不能直接实例化,主要用于制定一些规范。

内置抽象基类

假如我们希望自定义一个集合类型的类,例如类似tuple的类,那么我们就需要实现集合相关的魔法方法,如:__len__/__iter__/__contains__/,此时如果我们要判断这个类符不符合序列类的要求,可能就会进行这样的判断:

class A: pass

def is_sequence(cls):
    return hasattr(cls, "__len__") and hasattr(cls, "__iter__") and hasattr(cls, "__contains__")
    # 判断是否实现了序列类的所有魔法方法

print(is_sequence(A))

这样需要我们进行一系列的判断,编写判断条件也十分的麻烦。而在collections.abc模块下提供了很多内置数据类型结构的抽象基类,能够更加简单地帮我们判断是否为某一种数据类型,例如前面判断集合类的代码就能够改成:

from collections.abc import Collection
class A: pass

print(issubclass(A, Collection))

而实际上这些基类也是通过重写了issubclass行为的魔法方法,这里就拿集合的源码来分析:

class Collection(Sized, Iterable, Container):

    __slots__ = ()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Collection:
            return _check_methods(C,  "__len__", "__iter__", "__contains__")
            # _check_methods检查指定类的父类当中是否存在这些方法,源码放在下面
        return NotImplemented

def _check_methods(C, *methods):
    # 基于mro顺序遍历父类方法,查看指定的方法是否实现
    mro = C.__mro__
    for method in methods:
        for B in mro:
            if method in B.__dict__:
                if B.__dict__[method] is None:
                    return NotImplemented
                break
        else:
            return NotImplemented
    return True

可以看出其内部实现了__subclasshook__魔法方法来进行子类判断,所以只要实现了指定的魔法方法,就是当前基类的子类

collections.abc模块下提供的这些抽象基类除了前面的用处以外,也让我们能够更好地参考其提供的所有内置基本数据类型结构,下面是加注释的源码一览:

__all__ = [
    "Awaitable", 
    # await语法后面必须接的awaitable对象基类
    "Coroutine",
    # 协程基类
    "AsyncIterable",
    # 异步可迭代对象基类
    "AsyncIterator",
    # 异步迭代器基类,如使用async for语法必须实现该基类的方法
    "AsyncGenerator",
    # 异步生成器基类
    "Hashable",
    # 可哈希的对象基类,只有可哈希的对象才能存入像集合、字典的key当中
    "Iterable",
    # 可迭代的对象基类
    "Iterator",
    # 迭代器基类
    "Generator",
    # 生成器基类
    "Reversible",
    # 可逆序的基类(可以使用reversed方法)
    "Sized",
    # 存在size长度的基类(可以使用len方法)
    "Container",
    # 可判断是否存在某元素的基类(可以使用in语法)
    "Callable",
    # 可以当函数调用的基类
    "Collection",
    # 集合基类(这个集合代表可以存放多个元素,并且能迭代、获取尺寸、判断是否存在)
    "Set",
    # 不可变集合的基类,如frozenset
    "MutableSet",
    # 可变集合的基类,如set
    "Mapping",
    # 不可变映射的基类,如MappingProxyType
    "MutableMapping",
    # 可变映射的基类,如dict
    "MappingView",
    # 可获取映射类长度的基类,使映射类可以使用(len)方法
    "KeysView",
    # 可以迭代key的基类,例如使映射类可以使用keys()方法
    "ItemsView",
    # 可以迭代key, value的基类,例如使映射类可以使用items()方法
    "ValuesView",
    # 可以迭代value的基类,例如使映射类可以使用values()方法
    "Sequence",
    # 不可变序列的基类,如tuple
    "MutableSequence",
    # 可变序列的基类,如list
    "ByteString",
    # 字节数据相关的基类(实际上就是继承了不可变序列类),如bytes
]

property动态属性

通过property装饰器,我们可以设置动态属性,使得一个属性可以动态计算:

class A:
    def __init__(self, birth, year):
        self._birth = birth
        self._year = year
    
    @property
    def age(self):
        return self._year - self._birth

a = A(2000, 2020)
print(a.age)

# 20

可以看到我们访问age属性,是通过指定age方法获得的,因此使用property使我们能够将一个属性通过方法来计算获取

描述符

属性描述符

通过描述符相关的魔法方法(__set__/__get__/__delete__),可以定义对一个属性读/写/删操作的行为,例如通过属性描述符实现类型校验:

class IntField:
    def __get__(self, instance, owner):
        return self.val
    def __set__(self, instance, val):
        # 对age字段赋值时进行校验
        if not type(val) is int:
            raise TypeError
        self.val = val
    def __delete__(self, instance):
        self.val = None

class People:
    age = IntField()

p = People()
p.age = 100
p.age = "dasdas"

# TypeError
数据描述符/非数据描述符
  • 数据描述符:实现了__get____set__方法
  • 非数据描述符:只实现了__get__方法

举例:

class DataField:
    # 数据描述符
    def __get__(self, instance, owner):
        return self.val
    def __set__(self, instance, val):
        self.val = val
        
class NonDataField:
    # 非数据描述符
    def __get__(self, instance, owner):
        return "aaa"

两者除了一个可以进行操作,一个不行以外,在属性的查找顺序上也存在区别

调用属性执行顺序

调用属性时,会先调用__getattribute__方法(之后可能会调用的__get__/__getattr__等方法都是在该方法的处理逻辑当中调用的)
例如调用的属性不存在,此时如果定义了__getattr__方法,就会调用该方法,否则抛出AttributeError错误

完整逻辑如下:

  1. 如果属性是在其类对象或者基类的__dict__属性当中,并且属性是数据描述符,那么将调用__get__方法,否则进入2
  2. 如果属性是在当前实例对象的__dict__中,则直接返回__dict__[attr],否则进入3
  3. 如果属性是在其类对象或者基类的__dict__属性当中,并且属性是非数据描述符,则调用其__get__方法,否则进入4
  4. 如果属性是在其类对象或者基类的__dict__属性当中,并且属性不是描述符(没有实现__get__方法),则返回__dict__[attr],否则进入5
  5. 进入5则说明该属性不存在,此时如果当前类实现了__getattr__方法,则调用该方法,否则进入6
  6. 抛出AttributeError错误

举例:

class DataField:
    # 数据描述符
    def __get__(self, instance, owner):
        print("data...")
        return 100
    def __set__(self, instance, val):
        self.val = val
        
class NonDataField:
    # 非数据描述符
    def __get__(self, instance, owner):
        print("nondata...")
        return "aaa"


class People:
    name = NonDataField()
    age = DataField()
    def __init__(self):
        self.name = "bbb"
        self.age = 0

p = People()
print(p.name, p.age)

# data...
# bbb 100

可以看到这里定义了一个People类,里面的nameage属性分别是非数据描述符和数据描述符,然后实例化的时候又定义了nameage属性。
在调用这两个属性的时候,因为People类的__dict__当中存在数据描述符age,因此根据上面查找的第一步,调用了__get__方法,返回了属性值100;而name作为非数据描述符,且在当前的实例对象当中也有name属性,因此根据上面查找的第二步,返回了实例对象的name属性值bbb

元类编程

元类

元类就是能够创建类的类,而type函数能够创建类(需要传递三个参数:类名/基类/属性值):

>>> Test = type('Test', (object,), {'a':1})
# 创建名为Test,继承于object类,存在属性a的值为1的类
>>> test = Test()
>>> test.a
1

上面的创建方式等价于:

class Test(object):
    a = 1

因此type本身就是一个元类,而我们编写的类,只要其继承type,就是一个元类,通过元类,我们能够控制类实例化的过程(当一个类没有设置元类时,会默认使用type来创建该类)

实际上class语法本质上也就是相当于调用了type方法进行类的创建。而元类则可以理解成基于type方式创建对象的类,当在类当中指定元类以后,将会调用元类的方法,并在元类创建类时通过修改传入的类名、继承类、属性等方式来达到动态修改类的目的,举例:

class AddAttrMetaClass(type):
    def __new__(meta_class, class_name, class_parents, class_attrs):
        class_attrs['add_attr'] = '在元类里添加的属性'
        print("创建新类,类名:{}, 继承父类:{}, 存在属性:{}".format(class_name, class_parents, class_attrs))
        return type.__new__(meta_class, class_name, class_parents, class_attrs)

class Test(metaclass = AddAttrMetaClass):
    pass

test = Test()

# 结果:
# 创建新类,类名:Test, 继承父类:(), 存在属性:{'__module__': '__main__', '__qualname__': 'Test', 'add_attr': '在元类里添加的属性'}

可以看出上面代码中Test类指定了AddAttrMetaClass为其元类,因此在创建类时会先执行AddAttrMetaClass里的__new__方法,该方法通过修改class_attrs属性,从而达到修改创建的Test类中含有的属性。

动态创建类的方式
  • 在函数内部定义类(python解释器在初始化时只是声明函数,而函数内部的类则是在函数执行时的对应栈帧内创建,因此可以看做是一种动态地创建类),通过函数创建:
def create_class(cls):
    class A: pass
    class B: pass
    di = {
        "A": A,
        "B": B
        }
    if cls in di:
        return di[cls]
    return None

a = create_class("A")()
print(a)

# <__main__.create_class.<locals>.A object at 0x000001D83A2294A8>
  • 通过type创建,举例:
def y(self):
    print(self.x)

A = type("A", (), {"x": 1, "y": y})
# 创建A类
a = A()
# 实例化A类
a.y()
# 调用a对象的y方法
print(A)

# 1
# <class '__main__.A'>
类的实例化过程

python解释器在启动时,会先创建所有唯一的类对象,而这些类对象就是通过元类来进行创建,例如下面代码:

class M(type):
    def __new__(cls, name, bases, attrs):
        print(1)
        return super().__new__(cls, name, bases, attrs)

class A(metaclass=M):
    def __new__(cls):
        print(3)
        return super().__new__(cls)

print(2)
a = A()
a = A()

# 1
# 2
# 3
# 3

可以看出因为A指定了元类M,因此在解释器启动时,通过元类M创建了类对象A,因此输出1,然后才开始执行我们的主流程语句,输出2,之后再生成A的实例化对象时,只是对A的类进行实例化,没有创建类对象的操作,因此不再输出1,而是执行A的实例化方法,输出3

元类中__call__的作用

类都是通过的__call__创建的,因此显然__call__的本质是创建类,也就是调用类的__new____init__方法,举例:

class A(type):
    def __init__(self, object_or_name, bases, dict):
        super().__init__(object_or_name, bases, dict)
        print(1)
    def __new__(cls, name, base, attr):
        print(0)
        return super().__new__(cls, name, base, attr)
    def __call__(self, *args, **kwargs):
        print(2)
        return super().__call__(*args, **kwargs)

class B(metaclass=A):
    def __new__(cls):
        print(3)
        return super().__new__(cls)
    def __init__(self):
        print(4)

b = B()
print(b)

# 0
# 1
# 2
# 3
# 4
# <__main__.B object at 0x00000246D5B2F668>

可以发现如果把元类的__call__方法最后一行注释,则不会输出34,即不会创建B类了

type

type本身也是一个类,但他同时也是一个对象(自身的实例),因此可以看到type的基类是object,但是object又是由type生成,而type函数则是查看创建该类的类,举例:

>>> type(object)
<class 'type'>
# 创建object的类是type
>>> type(type)
<class 'type'>
# 创建type的类也是type
>>> type.__bases__
(<class 'object'>,)
# type也是继承于object
>>> class A(type): pass
>>> class B(metaclass=A): pass
>>> type(B)
<class '__main__.A'>
# B的元类是A,所以创建B的类是A

因为一切都是对象,而对象很容易修改,所以python中的类即使定义好了也可以动态修改(猴子补丁之类的),因为可以理解为修改的是type生成的对象

type/object/类之间关系

在Python中,一切皆对象,而type能够生成类(因此默认情况下类是type的对象),对象本身也是类的对象,而type则是自身的对象,举例:

>>> class A:pass
>>> a = A()
>>> type(a)
<class '__main__.A'>
# 生成a的类是A
>>> type(A)
<class 'type'>
# 可以看出生成A的类是type
>>> type(A) is type
True
>>> type(object)
<class 'type'>
# 生成object的类是type
>>> type(type)
<class 'type'>
# 生成type的类也是type
>>> b = 1
>>> type(b)
<class 'int'>
>>> type(int)
<class 'type'>
# 可以看出一般情况下,类都是type的对象

但实际上,类都是其元类的对象,举例:

>>> class B():pass
>>> class C(B):pass
>>> type(C)
<class 'type'>
# 可以看出C是type的对象
>>> class B(type):pass
# 元类需要继承自type类
>>> class C(metaclass=B):pass
>>> type(C)
<class '__main__.B'>
# 可以看出这次C是元类B的对象

因此一个类如果不指定元类,默认元类就是type

元类实现orm

很多ORM框架都是基于元类来实现的,这里我们简单模拟一下Django中的ORM操作:

class BaseField:
    """字段基类,实现一些公共方法"""
    def __get__(self, instance, owner):
        return self._val

    def __set__(self, instance, val):
        raise NotImplementedError

    def _check_number(self, val):
        """检查是否为数字"""
        if not val is None:
            if not issubclass(type(val), (int, float)):
                raise TypeError
            return True
        else:
            return False

    def _check_str(self, val):
        """检查是否为字符串"""
        if not val is None:
            if not issubclass(type(val), str):
                raise TypeError
            return True
        else:
            return False

class NumberField(BaseField):
    """数字校验字段类"""
    def __init__(self, col_name, min_val=None, max_val=None):
        self._val = None
        self.col_name = col_name
        self.min_val = min_val
        self.max_val = max_val
        self._check_number(min_val)
        self._check_number(max_val)

    def __set__(self, instance, val):
        if not self._check_number(val):
            raise ValueError("need number")
        if (not self.min_val is None) and val < self.min_val:
            raise ValueError("below the min_val")
        if (not self.max_val is None) and val > self.max_val:
            raise ValueError("over the max_val")
        self._val = val

class CharField(BaseField):
    """字符串校验字段类"""
    def __init__(self, col_name, max_len=None):
        self._val = None
        self.col_name = col_name
        self.max_len = max_len
        self._check_number(max_len)

    def __set__(self, instance, val):
        if not self._check_str(val):
            raise ValueError("need str")
        if (not self.max_len is None) and len(val) > self.max_len:
            raise ValueError("over the max_len")
        self._val = val

class ModelMeta(type):
    """model生成元类"""
    def __new__(cls, name, bases, attrs):
        if name == "BaseModel":
            return super().__new__(cls, name, bases, attrs)
        fields = {}
        # 存储所有字段属性
        for k, v in attrs.items():
            # 非Meta和以下划线开头的属性都添加进字段
            if not k.startswith("_") and k != "Meta":
                fields[k] = v
        _meta = attrs.get("Meta")
        table_name = name.lower()
        # meta类处理
        if not _meta is None:
            meta_table_name = getattr(_meta, "table_name", None)
            if not meta_table_name is None:
                table_name = meta_table_name
            del attrs["Meta"]
        # 将字段相关属性设置到类当中
        attrs["table_name"] = table_name
        attrs["_meta"] = _meta
        attrs["fields"] = fields
        return super().__new__(cls, name, bases, attrs)

class BaseModel(metaclass=ModelMeta):
    def __init__(self, *args, **kwargs):
        # 初始化将传入的配置注入到属性当中
        for k, v in kwargs.items():
            setattr(self, k, v)
        super().__init__()
    
    def save(self):
        """生成插入语句逻辑"""
        fields = []
        values = []
        for k, v in self.fields.items():
            col = v.col_name
            val = getattr(self, k, "")
            fields.append(col)
            values.append(val)
        if len(fields) < 1:
            raise AttributeError
        sql = "insert {}({}) value({});".format(self.table_name, str(fields)[1:-1].replace("'", ""), str(values)[1:-1])
        print(sql)

class People(BaseModel):
    age = NumberField(col_name="age", min_val=1, max_val=100)
    name = CharField(col_name="name", max_len=10)
    class Meta:
        table_name = "tb_people"


people = People(name="aaa", age=30)
people.save()

# insert tb_people(age, name) value(30, 'aaa');
元类实现单例模式
懒汉式

懒汉式只需要在实例化该类时再创建对应的实例,因此直接在__new__方法里控制单例的创建即可,举例:

import threading

class A:
    ins = None
    lock = threading.Lock()

    def __new__(cls):
        # 如果已经创建就直接返回ins
        if cls.ins is None:
            with cls.lock:
                # 假如多个线程同时创建对象,那么可能在同一时间,对应的ins都是none,
                # 因此都能进入这里,所以这里需要再进行一次判断是否ins已经创建
                if cls.ins is None:
                    cls.ins = super().__new__(cls)
        return cls.ins

a = A()
b = A()
print(a is b)

# True
饿汉式

饿汉式需要在一开始就创建好对应的对象,因此我们可以在元类当中控制——当创建类时就实例化当前类的对象,举例:

class Meta(type):
    def __new__(cls, object_or_name, bases, dict):
        cls = super().__new__(cls, object_or_name, bases, dict)
        # 在创建类时创建实例
        cls.ins = cls()
        return cls

class A(metaclass=Meta):
    def __new__(cls):
        if not getattr(cls, "ins", None):
            cls.ins = super().__new__(cls)
        return cls.ins

print(A.ins)
print(A() is A())

参考:一文带你完全理解Python中的metaclass

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

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,731评论 2 9
  • Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的...
    有一种再见叫青春阅读 574评论 0 3
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,719评论 0 10
  • 每—个百合也似的小花箭,却密密团簇而成青莲的样子,然而这是油菜花苞初妆成。 每一颗花苞都黄熟了,看谁开第一个。 你...
    铃兰空地阅读 236评论 0 0