面向对象 --- 进阶篇

  类的成员                                                                        

类的成员可以分为三大类 : 字段 , 方法和属性

  类成员 :   

      字段 : 普通字段    静态字段

      方法 : 普通方法    静态方法      类方法

      属性 : 普通属性

  注 : 所有成员中 , 只有普通字段的内容保存对象中 , 即 : 根据此类创建了多少对象 , 在内存中就有多少个普通字段 . 而其他的成员 , 则都是保存在类中 , 即 : 无论对象的多少 , 在内存中只创建一个 .

一 . 变量

  变量包括 : 实例变量(字段)和类变量(静态字段)

    * 实例变量(字段)属于对象

    * 类变量(静态字段)属于类

class Province:

    # 静态字段
    country = '中国'

    def __init__(self, name):

        # 普通字段
        self.name = name


# 直接访问普通字段
obj = Province('河北省')
print obj.name

# 直接访问静态字段
Province.country

字段的定义和使用
普通和静态的区别

  由上述代码可以看出[普通字段需要通过对象来访问]  [静态字段通过类访问]

    * 静态字段在内存中只保存一份

    * 普通字段在每个对象中都要保存一份

class Foo:
    # 类变量(静态字段)
    country = "中国"
                            
    def __init__(self,name):
        # 实例变量(字段)
        self.name = name  
                            
                            
    def func(self):
        pass
                
                
obj1 = Foo('小桃红')
obj2 = Foo('何润东')
                        
Foo.country
公有实例变量
# 无法访问 :
class Base(object):
    __secret = "受贿"

class Foo(Base):

    def func(self):
        print(self.__secret)
        print(Foo.__secret)


obj = Foo()
obj.func()


# 可以访问
class Base(object):
    __secret = "受贿"

    def zt(self):
        print(Base.__secret)


class Foo(Base):

    def func(self):
        print(self.__secret)
        print(Foo.__secret)


obj = Foo()
obj.zt()
私有实例变量

二 . 方法

  方法包括 : 普通方法 , 静态方法和类方法 , 三种方法在内存中都归属类 , 区别在于调用方式不同 . 

    * 实例方法 : 由对象调用 ; 至少一个self参数 ; 执行普通方法时 , 自动将调用该方法的对象赋值给self ;

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

    # 实例方法
    def func(self):
        print(self.name)
                                
obj = Foo('..')
obj.func()
实例

    * 类方法 : 由类调用 ; 至少一个cls参数 ; 执行类方法时 , 自动将调用该方法的类复制给cls ;

class Foo(object):

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

    # 静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法
    @staticmethod
    def display(a1,a2):
        return a1 + a2

Foo.display(1,3)
实例

    * 静态方法 : 由类调用 ; 无默认参数;

class Foo(object):
                        
    # 类方法,cls是类
    @classmethod
    def show(cls,x1,x2):
        print(cls,x1,x2)

# 执行类方法
Foo.show(1,8)
实例
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()

方法的定义和使用
方法和定义

  相同点 : 对于所有的方法而言 , 均属于类中 , 所以 , 在内存也只保存一份 .

  不同点 : 方法调用者不同 , 调用方法时自动传入的参数不同 .

三 . 属性

  属性就是实例方法的变种 .

  属性的知识点 :

    * 属性的基本使用

    * 实现的两种定义方式

  1 . 属性的基本使用

# ############### 定义 ###############
class Foo:

    def func(self):
        pass

    # 定义属性
    @property
    def prop(self):
        pass

# ############### 调用 ###############
foo_obj = Foo()

foo_obj.func()
foo_obj.prop   #调用属性
属性的定义和使用

  由属性的定义和调用要注意以下几点 :

    * 定义时 , 在普通方法的基础上面添加 @property 装饰器 ;

    * 定义时 , 属性仅有一个 self 参数

    * 调用时 , 无需括号

          方法 : foo_obj.func()

          属性 : foo_obj.prop

  注意 : 属性存在意义是 : 访问属性时可以制造出和访问字段完全相同的假象

       属性由方法变种而来 , 如果Python中没有属性 , 方法完全可以代替其功能 .

# 根据用户请求的当前页和总数据条数计算出 m 和 n
# 根据m 和 n 去数据库中请求数据 

# ############### 定义 ###############
class Pager:
    
    def __init__(self, current_page):
        # 用户当前请求的页码(第一页、第二页...)
        self.current_page = current_page
        # 每页默认显示10条数据
        self.per_items = 10 


    @property
    def start(self):
        val = (self.current_page - 1) * self.per_items
        return val

    @property
    def end(self):
        val = self.current_page * self.per_items
        return val

# ############### 调用 ###############

p = Pager(1)
# p.start 就是起始值,即:m
# p.end   就是结束值,即:n

  由上述可见 , Python的属性的功能是 : 属性内部进行一系列的逻辑计算 , 最终将计算结果返回

  2 . 属性的两种定义方式

    属性的定义有两种方式 : 

      装饰器 即 : 在方法上应用装饰器

      静态字段 即 : 在类中定义值为property对象的静态字段

  装饰器方式 : 在类的普通方法上应用@property装饰器

    经典类 : 

# ############### 定义 ###############    
class Goods:

    @property
    def price(self):
        return "wupeiqi"
# ############### 调用 ###############
obj = Goods()
result = obj.price  # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
经典类

    新式类 :

# ############### 定义 ###############
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 方法
新式类

  注:经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
         新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法

  由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除

class Goods(object):

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

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deltter
    def price(self, value):
        del self.original_price

obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
del obj.price     # 删除商品原价

实例
实例

  静态字段方式 ,创建之为property对象的静态字段

    当使用静态字段的方式创建属性时 , 经典类和新式类无区别

    property的构造方法中有四个参数:

      1 . 方法名 , 调用 对象.属性 时自动触发执行方法

      2 . 方法名 , 调用 对象.属性 = xxx 时自动触发执行方法

      3 . 方法名 , 调用 del 对象.属性 时自动触发执行方法.

      4 . 字符串 , 调用 对象.属性.__doc__ ,次参数是该属性的描述信息

class Foo:

    def get_bar(self):
        return 'wupeiqi'

    # *必须两个参数
    def set_bar(self, value): 
        return return 'set value' + value

    def del_bar(self):
        return 'wupeiqi'

    BAR = property(get_bar, set_bar, del_bar, 'description...')

obj = Foo()

obj.BAR              # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex"     # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
del Foo.BAR          # 自动调用第三个参数中定义的方法:del_bar方法
obj.BAE.__doc__      # 自动获取第四个参数中设置的值:description...
View Code

 类成员的修饰符                                                             

  对于每一个类的成员而言都有两种形式 : 

    * 公有成员 , 在任何地方都能访问

    * 私有成员 , 只有在类的内部才能访问

class C:
 
    def __init__(self):
        self.name = '公有字段'
        self.__foo = "私有字段"

    静态字段

  • 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
  • 私有静态字段:仅类内部可以访问;
    class C:
    
        name = "公有静态字段"
    
        def func(self):
            print C.name
    
    class D(C):
    
        def show(self):
            print C.name
    
    
    C.name         # 类访问
    
    obj = C()
    obj.func()     # 类内部可以访问
    
    obj_son = D()
    obj_son.show() # 派生类中可以访问
    
    公有静态字段
    公有
    class C:
    
        __name = "私有静态字段"
    
        def func(self):
            print C.__name
    
    class D(C):
    
        def show(self):
            print C.__name
    
    
    C.__name       # 类访问            ==> 错误
    
    obj = C()
    obj.func()     # 类内部可以访问     ==> 正确
    
    obj_son = D()
    obj_son.show() # 派生类中可以访问   ==> 错误
    
    私有静态字段
    私有

  普通字段

  •     公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
  •     私有普通字段:仅类内部可以访问;
    class C:
        
        def __init__(self):
            self.foo = "公有字段"
    
        def func(self):
            print self.foo  # 类内部访问
    
    class D(C):
        
        def show(self):
            print self.foo # 派生类中访问
    
    obj = C()
    
    obj.foo     # 通过对象访问
    obj.func()  # 类内部访问
    
    obj_son = D();
    obj_son.show()  # 派生类中访问
    
    公有字段
    公有
    class C:
        
        def __init__(self):
            self.__foo = "私有字段"
    
        def func(self):
            print self.foo  # 类内部访问
    
    class D(C):
        
        def show(self):
            print self.foo # 派生类中访问
    
    obj = C()
    
    obj.__foo     # 通过对象访问    ==> 错误
    obj.func()  # 类内部访问        ==> 正确
    
    obj_son = D();
    obj_son.show()  # 派生类中访问  ==> 错误
    
    私有字段
    私有

      私有成员只能在类内部使用

 类的组合                                                                          

  类或对象可以做字典中的key和value的

lass Foo:
    pass

user_info = {
    Foo:1,
    Foo():5
}

print(user_info)
View Code

  让我们看一看对象中到底有什么?

class StarkConfig(object):

    def __init__(self,num):
        self.num = num

    def changelist(self,request):
        print(self.num,request)

class RoleConfig(StarkConfig):

    def changelist(self,request):
        print('666')

# 创建了一个列表,列表中有三个对象(实例)
# [ StarkConfig对象(num=1), StarkConfig对象(num=2), RoleConfig对象(num=3) ]
config_obj_list = [StarkConfig(1),StarkConfig(2),RoleConfig(3)]
for item in config_obj_list:
    print(item.num)

# 结果
# 1
# 2
# 3
View Code
class UserInfo(object):
    pass

class Department(object):
    pass

class StarkConfig(object):

    def __init__(self,num):
        self.num = num

    def changelist(self,request):
        print(self.num,request)

    def run(self):
        self.changelist(999)

class RoleConfig(StarkConfig):

    def changelist(self,request):
        print(666,self.num)

class AdminSite(object):
    def __init__(self):
        self._registry = {}

    def register(self,k,v):
        self._registry[k] = v(k)

site = AdminSite()
site.register(UserInfo,StarkConfig)
site.register(Department,StarkConfig)
print(len(site._registry)) # 3
for k,row in site._registry.items():
    row.run()
实例

  总结 : 

    1. 对象中封装了什么?

    2. self到底是谁?

  只有明白这两个问题,就不会错

# ############# 创建学校 ##########
class School:
    def __init__(self, address):
        self.address = address

bj = School('北京校区')
sh = School('上海校区')
sz = School('深圳校区')

# ################ 创建课程 ##########
class Course(object):
    def __init__(self, name, period, price, school=None):
        self.name = name
        self.period = period
        self.price = price
        self.school = school
# ########## 学校和课程的组合 #########
py1 = Course('Python全栈', 110, 19999, bj)
py2 = Course('Python全栈', 110, 19999, sh)
py3 = Course('Python全栈', 110, 19999, sz)

l1 = Course('Linux运维', 110, 19999, bj)
l2 = Course('Linux运维', 110, 19999, sh)

g1 = Course('Go开发', 119, 19999, bj)


class Grade(object):
    def __init__(self, name, people, introduce, course=None):
        self.name = name
        self.people = people
        self.introduce = introduce
        self.course = course
        
# ######### 班级和课程的组合 #########
# 北京
gr1 = Grade('11期',20,'....',py1)
gr2 = Grade('15期',20,'....',py1)

gr3 = Grade('11期',20,'....',l1)
gr4 = Grade('12期',20,'....',l1)

gr5 = Grade('21期',20,'....',g1)
gr6 = Grade('65期',20,'....',g1)
# 上海
gr7 = Grade('1期',20,'....',l2)
gr8 = Grade('2期',20,'....',l2)

gr9 = Grade('1期',20,'....',py2)
gr10 = Grade('2期',20,'....',py2)
# 深圳
gr11 = Grade('1期',20,'....',py3)

# 查看哪个学校哪个课程哪个班级人数
print(gr1.people)
# 查看哪个学校哪个课程的学费
print(gr1.course.price)
# 查看哪个课程哪个班级在哪个学校
print(gr1.course.school.address)
组合的实例

  1 . 主动调用其他类的成员

class Base(object):

    def f1(self):
        print('5个功能')

class Foo(object):

    def f1(self):
        print('3个功能')
    
        Base.f1(self)        #调用别的类

obj = Foo()
obj.f1()            
方式一
class Foo(object):
    def f1(self):
        super().f1()     #按照类的继承顺序找下一个
        print('3个功能')

class Bar(object):
    def f1(self):
        print('6个功能')

class Info(Foo,Bar):
    pass



obj = Info()
obj.f1()

# 结果:
'''
6个功能
3个功能
'''
方式二

 类的特殊成员                                                                   

       上文介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下:

  1. __init__

    初始化方法 , 对空对象初始化 , [类名()]   自动触发执行

class Foo(object):

    def __init__(self,a1,a2):
        self.a1 = a1
        self.a2 = a2

obj = Foo(1,2)
View Code

  2. __call__

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

class Foo(object):

    def __init__(self,a1,a2):
        self.a1 = a1
        self.a2 = a2
    
    def __call__(self, *args, **kwargs):
        print(11111,args,kwargs)
        return 123

ret = obj(6,4,2,k1=456)
View Code

  3. __getitem__

      用于索引操作 , 自动触发执行     对象["xx"]

class Foo(object):

    def __init__(self,a1,a2):
        self.a1 = a1
        self.a2 = a2
    
    def __getitem__(self, item):
        print(item)
        return 8

ret = obj['yu']
print(ret)
View Code

  4. __setitem__

     用于索引操作 , 自动触发执行     对象["xx"] = 任意

class Foo(object):

    def __init__(self,a1,a2):
        self.a1 = a1
        self.a2 = a2

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

obj['k1'] = 123
View Code

  5. __delitem__

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

class Foo(object):

    def __init__(self,a1,a2):
        self.a1 = a1
        self.a2 = a2

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

obj = Foo(1,2)
del obj['uuu']
View Code

  6. __add__

    对象 + 对象  自动触发执行    

class Foo(object):

    def __init__(self,a1,a2):
        self.a1 = a1
        self.a2 = a2

    def __add__(self, other):
        return self.a1 + other.a2

obj1 = Foo(1,2)
obj2 = Foo(88,99)
ret = obj2 + obj1
print(ret)
View Code

  7. __enter__ / __exit__

    用于文件操作 , 自动触发执行    with 对象

class Foo(object):

    def __init__(self,a1,a2):
        self.a1 = a1
        self.a2 = a2

    def __enter__(self):
        print('1111')
        return 999

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('22222')

obj = Foo(1,2)
with obj as f:
    print(f)
    print('内部代码')
View Code

  8. __new__

    真正的构造方法

class Foo(object):
    def __init__(self, a1, a2):     # 初始化方法
        """
        为空对象进行数据初始化
        :param a1:
        :param a2:
        """
        self.a1 = a1
        self.a2 = a2

    def __new__(cls, *args, **kwargs): # 构造方法
        """
        创建一个空对象
        :param args:
        :param kwargs:
        :return:
        """
        return object.__new__(cls) # Python内部创建一个当前类的对象(初创时内部是空的.).
View Code

   9. __str__

class Foo(object):
    def __init__(self):
        pass

    def func(self):
        pass

    def __str__(self):
        return "F1"

obj = Foo()
print(obj,type(obj))

"""
结果:
F1  类Foo
"""
View Code

  10. __doc__

class Foo(object):
    """
    asdfasdfasdfasdf
    """
    def __init__(self):
        pass

    def func(self):
        pass

    def __str__(self):
        return "F1"

obj = Foo()
print(obj.__doc__)

"""
结果:
asdfasdfasdfasdf
"""
View Code

  11. __dict__

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

    def func(self):
        pass

obj1 = Foo('太白',99)
obj2 = Foo('彦涛',89)

print(obj1.__dict__) 
print(obj2.__dict__) 

"""
结果:
{'name': '太白', 'age': 99}
{'name': '彦涛', 'age': 89}
"""
View Code

  12. __iter__

# l1是list类的一个对象,可迭代对象
l1 = [11,22,33,44]

# l2是list类的一个对象,可迭代对象
l2 = [1,22,3,44]


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

    def func(self):
        pass

    def __iter__(self):
        # return iter([11,22,33,44,55,66])

        yield 11
        yield 22
        yield 33

# obj1是Foo类的一个对象,可迭代对象
"""
如果想要把不可迭代对象 -> 可迭代对象
1. 在类中定义__iter__方法
2. iter内部返回一个迭代器(生成器也是一种特殊迭代器)
"""
obj1 = Foo('刘博文',99)

for item in obj1:
    print(item)
View Code

  经典题型:

class StarkConfig(object):
    list_display = []

    def get_list_display(self):
        self.list_display.insert(0,33)
        return self.list_display

class RoleConfig(StarkConfig):
    list_display = [11,22]


s1 = RoleConfig()
s2 = RoleConfig()

result1 = s1.get_list_display()
print(result1)

result2 = s2.get_list_display()
print(result2)

"""
结果:
[33,11,22]
[33,33,11,22]
"""
View Code

  反射                                                                 

1. issubclass/type/isinstance

  a. issubclass

class Base(object):
    pass

class Foo(Base):
    pass

class Bar(Foo):
    pass

print(issubclass(Bar,Base)) # 检查第一个参数是否是第二个参数的 子子孙孙类
View Code

  b. type

    获取当前对象是由哪个类创建

class Foo(object):
    pass

obj = Foo()

print(obj,type(obj)) 

if type(obj) == Foo:
    print('obj是Foo类型')
View Code
class Foo(object):
    pass

class Bar(object):
    pass

def func(*args):
    foo_counter =0
    bar_counter =0
    for item in args:
        if type(item) == Foo:
            foo_counter += 1
        elif type(item) == Bar:
            bar_counter += 1
    return foo_counter,bar_counter

# result = func(Foo(),Bar(),Foo())
# print(result)

v1,v2 = func(Foo(),Bar(),Foo())
print(v1,v2)
例题

  c. isinstance

    isinstance(xx.xx)检查第一个参数(对象)是否由第二个参数(派生类及基类)的实例.

class Base(object):
    pass

class Foo(Base):
    pass

obj1 = Foo()
print(isinstance(obj1,Foo))   # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。
print(isinstance(obj1,Base))  # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。


obj2 = Base()
print(isinstance(obj2,Foo))   # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。
print(isinstance(obj2,Base))  # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。
View Code

2. 方法和函数

  对象.xxx  ----> xxx就是方法

  类.xxx      ----> xxx就是函数

  xxx          ----> xxx就是函数

  打印查看:

    function    函数

    method     方法

  代码检查:

from types import MethodType,FunctionType
def check(arg):
    """
    检查arg是方法还是函数?
    :param arg:
    :return:
    """
    if isinstance(arg,MethodType):
        print('arg是一个方法')
    elif isinstance(arg,FunctionType):
        print('arg是一个函数')
    else:
        print('不知道是什么')
View Code

3. 反射

  getattr   # 根据字符串的形式 , 去对象中找成员

import xx

v1 = getattr(xx,'x1')   # xx是一个模块
v2 = getattr(xx,'f1')
v2('杨森')
View Code

  hasattr  # 根据字符串的形式 , 去判断对象中是否由成员

v3 = hasattr(xx,'x1')
v4 = hasattr(xx,'f1')
v4 = hasattr(xx,'f1')
v5 = hasattr(xx,'xxxxxxx')
print(v3,v4,v5)
View Code

  setattr   # 根据字符串的形式 , 动态的设置一个成员(在内存中)

setattr(xx,'x2',999)
v6 = getattr(xx,'x2')
print(v6)
View Code
setattr(xx,'f2',lambda x:x+1)
v7 = getattr(xx,'f2')
v8 = v7(1)
print(v8)
View Code

  delattr   # 根据字符串的形式 , 动态的删除一个成员(在内存中)

delattr(xx,'x1')
v9 = getattr(xx,'x1')
print(v9)
View Code

  总结 : v = getattr(obj,"函数名或方法名") 

    根据字符串为参数(第二个参数) , 去对象(第一个参数)中寻找与之同名的成员.

from types import FunctionType
    import handler

    while True:
        print("""
        系统支持的函数有:
            1. f1
            2. f2
            3. f3
            4. f4
            5. f5
        """)
        val = input("请输入要执行的函数:")
                if hasattr(handler,val):
                    func_or_val = getattr(handler,val)     # 根据字符串为参数,去模块中寻找与之同名的成员。
                    if isinstance(func_or_val,FunctionType):
                        func_or_val()
                    else:
                        print(func_or_val)
                else:    
                    print('handler中不存在输入的属性名')        
应用一
class Foo(object):

    def __init__(self,a1):
        self.a1 = a1
        self.a2 = None

obj = Foo(1)


v1 = getattr(obj,'a1')
print(v1)

setattr(obj,'a2',2)


v2 = getattr(obj,'a2')
print(v2)
应用二
class Account(object):
    func_list = ['login', 'logout', 'register']

    def login(self):
        """
        登录
        :return:
        """
        print('登录111')

    def logout(self):
        """
        注销
        :return:
        """
        print('注销111')

    def register(self):
        """
        注册
        :return:
        """
        print('注册111')

    def run(self):
        """
        主代码
        :return:
        """
        print("""
            请输入要执行的功能:
                1. 登录
                2. 注销
                3. 注册
        """)

        choice = int(input('请输入要执行的序号:'))
        func_name = Account.func_list[choice-1]

        # func = getattr(Account,func_name) # Account.login
        # func(self)

        func = getattr(self, func_name)  # self.login
        func()

obj1 = Account()
obj1.run()

obj2 = Account()
obj2.run()
View Code

4. 判断对象是否可以被调用

def func():
    pass


class Foo(object):
    def __call__(self, *args, **kwargs):
        pass
    def func(self):
        pass
obj = Foo()


print(callable(func))
print(callable(Foo))
print(callable(obj))
View Code
原文地址:https://www.cnblogs.com/xiangweilai/p/9548642.html