Python基础4 - 面向对象

面向对象

在Python中:

  • 函数式编程:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
  • 面向对象编程:对函数进行分类和封装,让开发“更快更好更强...”

 

面向对象编程(Object Oriented Programming(OOP), 面向对象程序设计)- 类(Class)和对象(Object)的应用

 

创建类

class foo:
    def hi(self, arg):
        print(self, self.name, self.age, self.gender, arg)
        return 2

obj1 = foo()   #根据类foo创建对象obj1
obj1.name = 'alan'
obj1.age = 22
obj1.gender = 'male'
obj1.hi(666)   #<__main__.foo object at 0x10382ba58> alan 22 male 666
obj1.hi(666666)   #<__main__.foo object at 0x10402ba58> alan 22 male 666666


obj2 = foo()
obj2.name = 'charon'
obj2.age = 23
obj2.gender = 'male'
obj2.hi(888)   #<__main__.foo object at 0x10382bb00> charon 23 male 888

 

面向对象三大特性

一. 封装(Encapsulation):

封装,将内容封装到某个地方,以后再去调用被封装在某处的内容. (共用的变量/参数放到对象中, 之后调用只要加不同的变量/参数)

所以,在使用面向对象的封装特性时,需要:

  • 将内容封装到某处
    • 在下面例子中, 内容其实被封装到了对象 obj1 和 obj2 中
  • 从某处调用被封装的内容
    • 通过对象直接调用被封装的内容
    • 通过self间接调用被封装的内容
class Person:
    def __init__(self,name,age):   #构造函数
        self.n = name
        self.a = age
        self.b = 'o'

    def show(self):
        print('%s-%s-%s'%(self.n, self.a, self.b))

obj1 = Person('alan',18)
print(obj1.n)  #alan   通过对象直接调用被封装的内容   直接调用obj1对象的name属性
obj1.show()   #alan-18-o    通过self间接调用被封装的内容   Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.n是 alan ;self.a 是 18

obj2 = Person('charon',19)
obj2.show()   #charon-19-o

 self 是一个形式参数,当执行 obj1 = Person('alan', 18 ) 时,self 等于 obj1

                              当执行 obj2 = Foo('charon', 19 ) 时,self 等于 obj2

self代指, 调用方法的对象/实例(中间人), print出来为内存地址 

构造方法: - 在实例化/创建对象的时候, 内部python自动调用

对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容.

 

适用场合:

如果多个函数中有一些相同参数时,转换成面向对象

class Person:
    def __init__(self,n,a,g,fc):
        self.name = n
        self.age = a
        self.gender = g
        self.fightingCapacity = fc

    def multiplayer(self):
        '''多人游戏, 消耗500战斗力'''
        self.fightingCapacity -= 500

    def practice(self):
        '''自我修炼, 增加200战斗力'''
        self.fightingCapacity += 200

    def grassland(self):
        '''草原战斗, 消耗100战斗力'''
        self.fightingCapacity -=100

    def showDetail(self):
        temp = 'Name: %s; Gender: %s; Age: %s; Fighting Capacity: %s;'%(self.name, self.gender, self.age, self.fightingCapacity)
        print(temp)

#创建角色
player1 = Person('Charon','male', '18', 10000)
player2 = Person('Alan','male', '22', 5000)
player3 = Person('Yerin','female', '19', 2000)
#执行任务
player1.multiplayer()
player2.practice()
player3.grassland()
#显示信息
player1.showDetail()
player2.showDetail()
player3.showDetail()
#再次执行任务
player1.multiplayer()
player2.practice()
player3.grassland()
#显示信息
player1.showDetail()
player2.showDetail()
player3.showDetail()
View Code

 

二. 继承(Inheritance):

继承,子类可以继承父类的内容

class 父类:   #基类
pass

class 子类(父类):     #派生类
pass

对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法

class F:
    def f1(self):
        print('F.f1')
    def f2(self):
        print('F.f2')
class S(F):
    def s1(self):
        print('S.s1')
    def f2(self):   #重写
        F.f2(self)   #执行父类/基类中该方法
        #super(S, self).f2()   #执行父类/基类中该方法 (推荐)
        print('S.f2')

s = S()
s.s1()  #S.s1  s1中的self是形参, 代指s
s.f1()  #F.f1  self永远指该方法的调用者, 所以在这里代指s
s.f2()  #F.f2  S.f2

Python中支持多继承

Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先

#深度优先
class D:
    def bar(self):
        print 'D.bar'

class C(D):
    def bar(self):
        print 'C.bar'

class B(D):
    def bar(self):
        print 'B.bar'

class A(B, C):
    def bar(self):
        print 'A.bar'

a = A()
a.bar()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> D --> C
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了

#两者共同点就是调用时, A(B, C), 优先由左侧开始搜寻

#广度优先
class D(object):
    def bar(self):
        print 'D.bar'

class C(D):
    def bar(self):
        print 'C.bar'

class B(D):
    def bar(self):
        print 'B.bar'

class A(B, C):
    def bar(self):
        print 'A.bar'

a = A()
a.bar()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了

继承使用(看源码执行流程)

class BaseReuqest:

    def __init__(self):
        print('BaseReuqest.init')


class RequestHandler(BaseReuqest):
    def __init__(self):
        print('RequestHandler.init')
        BaseReuqest.__init__(self)

    def serve_forever(self):
        # self,是obj
        print('RequestHandler.serve_forever')
        self.process_request()

    def process_request(self):
        print('RequestHandler.process_request')

class Minx:

    def process_request(self):
        print('minx.process_request')


class Son(Minx, RequestHandler):
    pass


obj = Son() # init     RequestHandler.init   BaseReuqest.init
obj.serve_forever()   #RequestHandler.serve_forever    minx.process_request
#1. obj = Son()
#2. RequestHandler's __init__()'s print
#3. BaseReuqest's __init__()'s print
#4. obj.serve_forever()
#5. serve_forever()'s print
#6. obj's process_request() 因为是对象obj的方法, 所以以继承的方式再从头开始找



# import socketserver
#
# 看源码执行流程
# obj = socketserver.ThreadingTCPServer(1,2) # 创建对象,init
# obj.serve_forever()
View Code

 

三. 多态(Polymorphism):

在python中是原生多态, 所以无需考虑
让变量/参数的值可以有多种形态(参数没有类型限制), python的多态是原生的  
# Java

String v = 'alex' //Java中的变量有类型限制

void func(String arg){
    print(arg)
} 
func('alex') //参数一定要为String
func(123) //ERROR

#---------------
class F{
pass
}
class S extends F {
pass
}

//声明func函数, 函数只接收F类或F类的子类的对象
void func(F arg){
print(arg);
}
//obj = F()
//obj = S()
func(obj)
View Code

# Python

v = 'alan'     #没有类型限制

def func(arg):
    print(arg)

func(1)    #传参可以是任意类型
func('alan')
View Code

 

一般在Python开发中,全部使用面向对象 或 面向对象和函数式混合使用

面向对象的应用场景:

  1. 多函数需使用共同的值,如:数据库的增、删、改、查操作都需要连接数据库字符串、主机名、用户名和密码
    class SqlHelper:
    
        def __init__(self, host, user, pwd):
    
            self.host = host
            self.user = user
            self.pwd = pwd
    
        def 增(self):
            # 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
            # do something
            # 关闭数据库连接
    
        def 删(self):
            # 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
            # do something
            # 关闭数据库连接
    
        def 改(self):
            # 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
            # do something
            # 关闭数据库连接
    
        def 查(self):
        # 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
            # do something
            # 关闭数据库连接# do something
    View Code
  2. 需要创建多个事物,每个事物属性个数相同,但是值的需求不同. 如:之前Person例子, 有name,age,bloodtype相同属性, 但是值不同

 

面相对像就是类和对象的应用, 类和对象在内存中的保存方式:


面向对象进阶

类成员

  • 字段
    • 普通字段
    • 静态字段
  • 方法
    • 普通方法
    • 静态方法
    • 类方法
  • 属性
    • 普通属性

 

一. 字段

- 普通字段,保存在对象中,执行只能通过对象访问
- 静态字段,保存在类中, 执行 可以通过对象访问 也可以通过类访问

class Province:
    # 静态字段, 属于类
    country = '中国'

    def __init__(self, name):
        # 普通字段, 属于对象
        self.name = name

# 通过对象访问普通字段
obj = Province('河北省')
print obj.name

# 直接访问静态字段
Province.country
obj.Province #通过对象访问静态字段

应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

 

二. 方法 

- 普通方法,保存在类中,由对象来调用,至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self
- 静态方法,保存在类中,由类直接调用 (self参数不一定要, 添加装饰器@staticmethod, 通过类可以直接调用)
- 类方法,保存在类中,由类直接调用,至少一个cls参数;执行类方法时,自动将调用该方法的复制给cls (添加装饰器@classmethod, 参数为cls(当前类名))

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

    def ord_func(self):
        """ 定义普通方法,至少有一个self参数 """
        # print(self.name)
        print('普通方法')

    @classmethod
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print('类方法')

    @staticmethod
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print('静态方法')


# 调用普通方法
f = Foo()
f.ord_func()

# 调用类方法
Foo.class_func()

# 调用静态方法
Foo.static_func()

应用场景:
如果对象中需要保存一些值,执行某功能时,需要使用对象中的值 -> 普通方法
不需要任何对象中的值 -> 静态方法/类方法

 

三. 属性

- 不伦不类 (写的时候按照方法的方式写, 调用的时候按照字段的方式调用(即调用时不需括号))

  • 定义时,在普通方法的基础上添加 @property 装饰器;
  • 定义时,属性仅有一个self参数
  • 调用时,无需括号

两种定义方式

1. 装饰器方式

# ############### 定义 ###############
class Goods(object):
    @property
    def price(self):
        print('@property')

    @price.setter
    def price(self, value):
        print('@price.setter')

    @price.deleter
    def price(self):
        print('@price.deleter')

# ############### 调用 ###############
obj = Goods()
obj.price          # 自动执行 @property 修饰的 price 方法,并获取方法的返回值, 一一对应的关系
obj.price = 123    # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数, 一一对应的关系
del obj.price      # 自动执行 @price.deleter 修饰的 price 方法, 一一对应的关系

2. 静态字段方式

class Goods(object):
    def __init__(self):
        self.original_price = 100        # 原价
        self.discount = 0.8        # 折扣

    def get_price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        print('the price after discount: ', new_price)
        return new_price
    
    #必须要有两个参数至少
    def set_price(self, value):
        self.original_price = value
        print('set original price is: ',self.original_price)

    def del_price(self):
        del self.original_price
        #print(self.original_price)    # AttributeError: 'Goods' object has no attribute 'original_price'

    PRICE = property(get_price, set_price, del_price, '价格属性描述...')

obj = Goods()
obj.PRICE         # 获取商品价格  自动调用第一个参数中定义的方法:get_price
obj.PRICE = 200   # 修改商品原价   自动调用第二个参数中定义的方法:set_price方法,并将 200 当作参数传入
del obj.PRICE     # 删除商品原价  自动调用第三个参数中定义的方法:del_price方法

 


类成员的修饰符

 

成员修饰符

  • 共有成员
  • 私有成员     __字段名
    • 无法直接访问,只能间接访问

静态字段

class Foo:
    __v = '110'   #静态字段变为私有变量, 外部无法访问
    def __init__(self, name, age):
        self.name = name
        self.__age = age   #普通字段变为私有变量, 外部无法访问

    def showNormal(self):
        return self.__age

    def showStatic(self):   #通过方法可以访问私有变量
        return Foo.__v

    @staticmethod     #静态方法也可以访问私有变量
    def stat():
        return Foo.__v


obj = Foo('alan', 18)
print(obj.name)   #alan
#print(obj.__age)  #AttributeError: 'Foo' object has no attribute '__age'

ret = obj.showNormal()
print(ret)   #18
ret = obj.showStatic()
print(ret)   #110
ret = obj.stat()
print(ret)   #110

静态方法

class Foo:
    def __f1(self):
        return 123

    def f2(self):
        r = self.__f1()
        return r

obj = Foo()
ret = obj.f2()
print(ret)   #123

在继承中的情况

class F:
    def __init__(self):
        self.ge = 123
        self.__ge = 1234

    def show(self):
        print(self.__ge)


class S(F):
    def __init__(self, n):
        self.name = n
        self.__age = 18
        super(S, self).__init__()

    def show(self):
        print(self.name)
        print(self.__age)
        print(self.ge)
#        print(self.__ge)  #AttributeError: 'S' object has no attribute '_S__ge'  父类的私有变量在子类也不能直接访问
        super(S, self).show()

s = S('alan')
s.show()

 


特殊类成员

 1. __doc__

表示类的描述信息

class Foo:
    """ 描述类信息, 我会被print出来 """
    def func(self):
        pass

print(Foo.__doc__)   #描述类信息, 我会被print出来 
View Code

 

2. __del__

析构方法,当对象在内存中被释放时,自动触发执行

class Foo:
    def __del__(self):
        pass
View Code

 

3. __init__

构造方法,通过类创建对象时,自动触发执行

 

4. __call__

对象后面加括号,触发执行

class Foo:
    def __init__(self):
        print('init')
    def __call__(self, *args, **kwargs):
        print('call')

obj = Foo()   # init
obj()    # call   对象加(), 会自动执行__call__方法
Foo()()   # init call
View Code

 

5. __int__ 和 __str__

int(对象), 自动执行对象的__int__方法, 并将返回值赋给int对象

str(对象), 自动执行对象的__str__方法, 并将返回值赋给str对象

class Foo:
    def __init__(self):
        pass
    def __int__(self):
        return 111
    def __str__(self):
        return 'charon'
obj = Foo()
print(obj, type(obj))   #<__main__.Foo object at 0x10381acc0> <class '__main__.Foo'>
i = int(obj)   #int(对象), 自动执行对象的__int__方法, 并将返回值赋给int对象
print(i)   #111
s = str(obj)   #str(对象), 自动执行对象的__str__方法, 并将返回值赋给str对象
print(s)   #charon
View Code
class Foo:
    def __init__(self,n,a):
        self.name = n
        self.age = a

    def __str__(self):
        return '%s - %s'%(self.name, self.age)

obj = Foo('charon', 18)
print(obj)   #charon - 18
#print(obj) 实际上等于 print(str(obj)), str(obj)自动执行了obj的__str__方法, 并将返回值赋给obj
View Code

 

6. __add__

两个对象相加时, 自动执行第一对象的__add__方法, 并且将第二个对象当作参数传递进去

class Foo:
    def __init__(self, n, a):
        self.name = n
        self.age = a

    def __add__(self,other):
        # self = ob1  (charon,18)
        # other = ob2  (charon,22)
        return self.age+other.age

obj1 = Foo('charon' ,18)
obj2 = Foo('charon' ,22)
print(obj1+obj2)   #40
#两个对象相加时, 自动执行第一对象的__add__方法, 并且将第二个对象仿作参数传递进去
View Code

 

7. __dict__

将对象中封装的所有内容通过字典的形式返回

class Foo:
    def __init__(self, n, a):
        self.name = n
        self.age = a
        self.n = 123

obj = Foo('charon' ,18)
d = obj.__dict__   #将对象中封装的所有内容通过字典的形式返回
print(d)   #{'name': 'charon', 'age': 18, 'n': 123}
View Code

 

8. __getitem__ 和 __setitem__ 和 __delitem__

class Foo:
    def __init__(self, n, a):
        self.name = n
        self.age = a
    def __getitem__(self, item):
        if type(item) == slice:
            print('这是切片处理')
            print(item.start)
            print(item.step)
            print(item.stop)
        else:
            print('这是索引处理')
            return item+10

    def __setitem__(self, key, value):
        print(key,value)

    def __delitem__(self, key):
        print(key)

obj = Foo('charon',18)
#索引(index)
r = obj[3]   #自动执行obj对象的类中的__getitem__方法, 3当做参数传递给item
print(r)   #这是索引处理 13
obj[100] = 'asdf'   #100 asdf    自动执行obj对象的类中的__setitem__方法, 100和asdf分别作为key和value传入
del obj[666]   #666     自动执行obj对象的类中的__delitem__方法, 666当做参数传递给key
#切片(slice)
obj[1:4:2]   #1 2 4 这是切片处理
View Code

 

9. __iter__

用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__

如果类中有__iter__方法, 对象->可迭代对象
对象.__iter__() 的返回值 是迭代器
for循环, 遇到迭代器, next()
for循环, 遇到可迭代对象, 对象.__iter__()变成迭代器, 然后next()

class Foo(object):
    def __init__(self, sq):
        self.sq = sq

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

obj = Foo([11,22,33,44])

# 1. 执行obj对象的类中的__iter__方法, 并获取其返回值
# 2. 循环上一步中返回的对象
for i in obj: 
    print i
View Code
obj = iter([11,22,33,44])

while True:
    val = obj.next()
    print val
For循环语法内部

 

10. __new__ 和 metaclass

Python中一切事物都是对象

class Foo:
    pass

obj = Foo()
# obj是对象 - Foo类的对象
# Foo类也是一个对象 - type的对象

obj对象是Foo类的一个实例, Foo类对象是 type 类的一个实例, 即:Foo类对象, 是通过type类的构造方法创建.

print type(f) # 输出:<class '__main__.Foo'>     表示,obj 对象由Foo类创建
print type(Foo) # 输出:<type 'type'>              表示,Foo类对象由 type 类创建

类都是type类的对象 - type(..)       
'对象'都是类的对象 - 类()

创建类就可以有两种方式:

1. 普通方式

class Foo(object):
    def func(self):
        print('hello charon')

2. 特殊方式

def func(self):
    print('hello charon')
  
Foo = type('Foo',(object,), {'func': func})
#type第一个参数:类名
#type第二个参数:当前类的基类
#type第三个参数:类的成员
def func(self):
    print("hello %s"%self.name)

def __init__(self,name,age):
    self.name = name
    self.age = age
Foo = type('Foo',(object,),{'func':func,'__init__':__init__})

f = Foo("charon",22)
f.func()
加上构造方法

类是由 type 类实例化产生 !!!

类默认是由 type 类实例化产生, 那么type类中如何实现的创建类?类又是如何创建对象?

答: 类中有一个属性 metaclass, 用来表示该类由谁来实例化创建. 所以, 可以为 metaclass 设置一个type类的子类, 从而查看类创建的过程

以下是手动创建类的过程 (自定义元类)

class MyType(type):
    def __init__(self, *args, **kwargs):   #这里的self指的是Foo类
        print('type init')
    def __call__(self, *args, **kwargs):   #这里的self指的是Foo类
        print('type call')
        obj = self.__new__(self, *args, **kwargs)  #这里的self指的是Foo类, 调用Foo类的__new__方法, 创建一个新的Foo类对象
        self.__init__(obj)   #这里的self指的是Foo类, 调用Foo类的__init__方法


class Foo(object,metaclass=MyType):
    def __init__(self):
        print('foo init')
    def f(self):
        print(234)
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls,*args,**kwargs)

# 第一阶段:解释器从上到下执行代码创建Foo类    (Foo类找到MyType类, MyType的__init__自动执行)

# 第二阶段:通过Foo类创建obj对象
obj = Foo()   #这句等于type对象加(), 所以执行__call__方法
View Code

类的生成 调用 顺序依次是 __new__ --> __init__ --> __call__

metaclass diagram

metaclass详解: 参考

A metaclass is the class of a class. Like a class defines how an instance of the class behaves, a metaclass defines how a class behaves. A class is an instance of a metaclass.


While in Python you can use arbitrary callables for metaclasses (like Jerub shows), the more useful approach is actually to make it an actual class itself. type is the usual metaclass in Python. In case you're wondering, yes, type is itself a class, and it is its own type. You won't be able to recreate something like type purely in Python, but Python cheats a little. To create your own metaclass in Python you really just want to subclass type.

A metaclass is most commonly used as a class-factory. Like you create an instance of the class by calling the class, Python creates a new class (when it executes the 'class' statement) by calling the metaclass. Combined with the normal __init__ and __new__ methods, metaclasses therefore allow you to do 'extra things' when creating a class, like registering the new class with some registry, or even replace the class with something else entirely.

When the class statement is executed, Python first executes the body of the class statement as a normal block of code. The resulting namespace (a dict) holds the attributes of the class-to-be. The metaclass is determined by looking at the baseclasses of the class-to-be (metaclasses are inherited), at the __metaclass__ attribute of the class-to-be (if any) or the __metaclass__ global variable. The metaclass is then called with the name, bases and attributes of the class to instantiate it.

However, metaclasses actually define the type of a class, not just a factory for it, so you can do much more with them. You can, for instance, define normal methods on the metaclass. These metaclass-methods are like classmethods, in that they can be called on the class without an instance, but they are also not like classmethods in that they cannot be called on an instance of the class. type.__subclasses__() is an example of a method on the type metaclass. You can also define the normal 'magic' methods, like __add__, __iter__ and __getattr__, to implement or change how the class behaves.

Here's an aggregated example of the bits and pieces:

def make_hook(f):
    """Decorator to turn 'foo' method into '__foo__'"""
    f.is_hook = 1
    return f

class MyType(type):
    def __new__(mcls, name, bases, attrs):

        if name.startswith('None'):
            return None

        # Go over attributes and see if they should be renamed.
        newattrs = {}
        for attrname, attrvalue in attrs.iteritems():
            if getattr(attrvalue, 'is_hook', 0):
                newattrs['__%s__' % attrname] = attrvalue
            else:
                newattrs[attrname] = attrvalue

        return super(MyType, mcls).__new__(mcls, name, bases, newattrs)

    def __init__(self, name, bases, attrs):
        super(MyType, self).__init__(name, bases, attrs)

        # classregistry.register(self, self.interfaces)
        print "Would register class %s now." % self

    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass
        # Alternatively, to autogenerate the classname as well as the class:
        # return type(self.__name__ + other.__name__, (self, other), {})

    def unregister(self):
        # classregistry.unregister(self)
        print "Would unregister class %s now." % self

class MyObject:
    __metaclass__ = MyType


class NoneSample(MyObject):
    pass

# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)

class Example(MyObject):
    def __init__(self, value):
        self.value = value
    @make_hook
    def add(self, other):
        return self.__class__(self.value + other.value)

# Will unregister the class
Example.unregister()

inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()

print inst + inst
class Sibling(MyObject):
    pass

ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__
View Code

 

posted @ 2018-01-23 11:49  Charonnnnn  阅读(150)  评论(0)    收藏  举报