面向对象进阶

面向对象进阶

 

类型判断

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的类

__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__)

 

__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可以调用

__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('嘿嘿嘿')

__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)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2022-08-04 14:10  布衣梦蝶1978  阅读(18)  评论(0编辑  收藏  举报