面向对象进阶

1. 类型判断

  • issubclass
    issubclass() 这个内置函数可以帮我们判断x类是否是y类型的子类
class Base:
    pass


class Foo(Base):
    pass


class Bar(Foo):
    pass

print(issubclass(Bar, Foo))   # True
print(issubclass(Foo, Bar))   # False
print(issubclass(Bar, Base))  # True
  • type
    type(obj) 表示查看 obj 是由哪个类创建的
class Foo:
    pass

obj = Foo()
print(obj, type(obj))  # 查看obj的类
  • isinstance
    isinstance 可以判断x是否是y类的对象
    isinstance 可以判断该对象是否是家族体系中的(只能往上判断类)
class Base:
    pass

class Foo(Base):
    pass

class Bar(Foo):
    pass

print(isinstance(Foo(), Foo))   # True
print(isinstance(Foo(), Base))  # True
print(isinstance(Foo(), Bar))   # False

2. 反射

反射的四个函数

  • hasattr(obj, str) 判断 obj 中是否包含str成员
  • getattr(obj,str) 从 obj 中获取str成员。
  • setattr(obj, str, value) 把 obj 中的 str 成员设置成 value。这⾥的value可以是值,也可以是函数或者⽅法。
  • delattr(obj, str) 把 obj中 的 str 成员删除掉。

3. 双下方法

_ str_

改变对象的字符串显示。可以理解为使用print函数打印一个对象时,会自动调用对象的__str__方法

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    # 定义对象的字符串表示
    def __str__(self):
        return self.name
    
s1 = Student('张三', 24)
print(s1)  # 会调用s1的__str__方法

_repr_

在python解释器环境下,会默认显示对象的repr表示。

>>> class Student:
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
...     def __repr__(self):
...         return self.name
... 
>>> s1 = Student('张三', 24)
>>> s1
张三

总结:

str函数或者print函数调用的是 obj. _str_()
repr函数或者交互式解释器调用的是 obj. _repr_()

注意:
如果 _str_ 没有被定义,那么就会使用 _repr_ 来代替输出。
_str_ 和 _repr_ 方法的返回值都必须是字符串。

_format_

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

    __format_dict = {
        'n-a': '名字是:{obj.name}-年龄是:{obj.age}',  # 名字是:lqz-年龄是:18
        'n:a': '名字是:{obj.name}:年龄是:{obj.age}',  # 名字是:lqz:年龄是:18
        'n/a': '名字是:{obj.name}/年龄是:{obj.age}',  # 名字是:/年龄是:18
    }

    def __format__(self, format_spec):
        if not format_spec or format_spec not in self.__format_dict:
            format_spec = 'n-a'
        fmt = self.__format_dict[format_spec]
        print(fmt) #{obj.name}:{obj.age}
        return fmt.format(obj=self)

s1 = Student('lqz', 24)
ret = format(s1, 'n/a')
print(ret)  # lqz/24

_ del_

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

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

class A:
    def __del__(self):
        print('删除了...')

a = A()
print(a)  # <__main__.A object at 0x10164fb00>
del a  # 删除了...
print(a)  # NameError: name 'a' is not defined

_dict_ 和 _slots_

Python中的类,都会从object里继承一个__dict__属性,这个属性中存放着类的属性和方法对应的键值对。一个类实例化之后,这个类的实例也具有这么一个__dict__属性。但是二者并不相同。

class A:
    some = 1

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

a = A(10)
print(a.__dict__)  # {'num': 10}
a.age = 10
print(a.__dict__)  # {'num': 10, 'age': 10}

从上面的例子可以看出来,实例只保存实例的属性和方法,类的属性和方法它是不保存的。正是由于类和实例有 _dict_ 属性,所以类和实例可以在运行过程动态添加属性和方法。

但是由于每实例化一个类都要分配一个 _dict_ 变量,容易浪费内存。因此在Python中有一个内置的 _slots_ 属性。当一个类设置了 _slots_ 属性后,这个类的 _dict_ 属性就不存在了(同理,该类的实例也不存在 _dict_ 属性),如此一来,设置了 _slots_ 属性的类的属性,只能是预先设定好的。

当你定义 _slots_ 后, _slots_ 就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的小型数组来构建的,而不是为每个实例都定义一个 _dict_ 字典,在 _slots_ 中列出的属性名在内部被映射到这个数组的特定索引上。使用 _slots_ 带来的副作用是我们没有办法给实例添加任何新的属性了。

注意:尽管 _slots_ 看起来是个非常有用的特性,但是除非你十分确切的知道要使用它,否则尽量不要使用它。比如定义了 _slots_ 属性的类就不支持多继承。 _slots_ 通常都是作为一种优化工具来使用。–摘自《Python Cookbook》8.4

class A:
    __slots__ = ['name', 'age']
    
a1 = A()
# print(a1.__dict__)  # AttributeError: 'A' object has no attribute '__dict__'
a1.name = '张三'
a1.age = 24
# a1.hobby = '泡妞'  # AttributeError: 'A' object has no attribute 'hobby'
print(a1.__slots__)

注意事项:
_slots_ 的很多特性都依赖于普通的基于字典的实现。
另外,定义了 _slots_ 后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义 _slots_,比如在程序中需要创建某个类的几百万个实例对象 。
关于 _slots_ 的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用 _slots_ 可以达到这样的目的,但是这个并不是它的初衷。它更多的是用来作为一个内存优化工具。

__item__系列

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

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        print('obj[key]=lqz赋值时,执行我')
        self.__dict__[key] = value

    def __delitem__(self, key):
        print('del obj[key]时,执行我')
        self.__dict__.pop(key)

    def __delattr__(self, item):
        print('del obj.key时,执行我')
        self.__dict__.pop(item)


f1 = Foo('sb')
print(f1.__dict__)
f1['age'] = 18
f1.hobby = '泡妞'
del f1.hobby
del f1['age']
f1['name'] = 'lqz'
print(f1.__dict__)

_ init _

使用Python写面向对象的代码的时候我们都会习惯性写一个 init 方法,init 方法通常用在初始化一个类实例的时候。例如:

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

    def __str__(self):
        return '<Person: {}({})>'.format(self.name, self.age)

p1 = Person('张三', 24)
print(p1)

上面是__init__最普通的用法了。但是__init__其实不是实例化一个类的时候第一个被调用的方法。当使用 Persion(name, age) 来实例化一个类时,最先被调用的方法其实是 new 方法。

_ new _

其实__init__是在类实例被创建之后调用的,它完成的是类实例的初始化操作,而 __new__方法正是创建这个类实例的方法

class Person:

    def __new__(cls, *args, **kwargs):
        print('调用__new__,创建类实例')
        return super().__new__(Person)

    def __init__(self, name, age):
        print('调用__init__,初始化实例')
        self.name = name
        self.age = age

    def __str__(self):
        return '<Person: {}({})>'.format(self.name, self.age)

p1 = Person('张三', 24)
print(p1)

输出:

调用__new__,创建类实例
调用__init__,初始化实例
<Person: 张三(24)>

_new_ 方法在类定义中不是必须写的,如果没定义的话默认会调用object._new_ 去创建一个对象(因为创建类的时候默认继承的就是object)。

如果我们在类中定义了 _new_ 方法,就是重写了默认的 _new_ 方法,我们可以借此自定义创建对象的行为

举个例子:

重写类的__new__方法来实现单例模式。

class Singleton:
    # 重写__new__方法,实现每一次实例化的时候,返回同一个instance对象
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = super().__new__(Singleton)
        return cls._instance

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


s1 = Singleton('张三', 24)
s2 = Singleton('李四', 20)
print(s1, s2)  # 这两实例都一样
print(s1.name, s2.name)

_ call _

_call_ 方法的执行是由对象后加括号触发的,即:对象()。拥有此方法的对象可以像函数一样被调用。

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

    def __call__(self, *args, **kwargs):
        print('调用对象的__call__方法')

a = Person('张三', 24)  # 类Person可调用
a()  # 对象a可以调用

注意:

_new_、_init_、__call__等方法都不是必须写的。

_doc_
定义类的描述信息。注意该信息无法被继承。

class A:
    """我是A类的描述信息"""
    pass

print(A.__doc__)

_iter_ 和 _next_
如果一个对象拥有了__iter__和__next__方法,那这个对象就是可迭代对象

class A:
    def __init__(self, start, stop=None):
        if not stop:
            start, stop = 0, start
        self.start = start
        self.stop = stop

    def __iter__(self):
        return self

    def __next__(self):
        if self.start >= self.stop:
            raise StopIteration
        n = self.start
        self.start += 1
        return n

a = A(1, 5)
from collections import Iterator
print(isinstance(a, Iterator))

for i in A(1, 5):
    print(i)

for i in A(5):
    print(i)
aaa=A(1)
print(next(aaa))
print(next(aaa)) #抛异常

_enter_ 和 _exit_
一个对象如果实现了__enter__和___exit__方法,那么这个对象就支持上下文管理协议,即with语句

class A:
    def __enter__(self):
        print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量')
        return 'oo'

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('退出with代码块时执行此方法')
        print('1', exc_type)
        print('2', exc_val)
        print('3', exc_tb)

with A() as f:
    print('进入with语句块')
    # with语句中代码块出现异常,则with后的代码都无法执行。
    # raise AttributeError('sb')
    print(f) #f打印出oo
print('嘿嘿嘿')

上下文管理协议适用于那些进入和退出之后自动执行一些代码的场景,比如文件、网络连接、数据库连接或使用锁的编码场景等。

_len_
拥有__len__方法的对象支持len(obj)操作。

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

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

a = A()
print(len(a))

_hash_
拥有 _hash_ 方法的对象支持hash(obj)操作。

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

    def __hash__(self):
        return hash(str(self.x) + str(self.x))

a = A()
print(hash(a))

_eq_
拥有 _eq_ 方法的对象支持相等的比较操作

class A:
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __eq__(self,obj):
        # 打印出比较的第二个对象的x值
        print(obj.x)
        if self.x +self.y == obj.x+obj.y:
            return True
        else:
            return False

a = A(1,2)
b = A(2,1)
print(a == b)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值