zoukankan      html  css  js  c++  java
  • 面向对象高阶

    面向对象高阶

    isinstance

    • 判断是否为实例化对象,以后可以用来取代type
    • 和type的区别:
    class Foo:
        pass
    
    class Bar(Foo):
        pass
    
    b = Bar()
    print(isinstance(b, Bar))	# True  未来推荐使用
    print(isinstance(b, Foo))	# True  会检测父类
    
    print(type(b) == type(Bar))	# False 	type(b)是Bar
    print(type(b) == type(Foo))	# False
    
    print(type(b) == Foo)		# False
    print(type(b) == Bar)		# True
    
    • isinstance:判断对象是否是这个类实例化出来的,实例化具有传递性,会检测父类
    • type:获取实例化出这个对象的类,不会检测父类

    issubclass

    • 判断某个类是否是另一个类的子类(参数必须为类)
    print(issubclass(Bar, Foo))	# True
    

    反射(自省)

    • 通过字符串去访问对象的属性,调用对象的方法,而Python中一切皆对象,都可以使用反射

    模块的使用

    import time
    
    choice = input('choice func:>>>').strip()
    print(getattr(time, choice)())
    
    # 其实就是这个用法
    # getattr(module, method)()
    

    放在类的使用

    1. hasattr
    2. getattr
    3. setattr
    4. delattr
    class Foo():
        count = 0
        def eat(self):
            print('eat')
            
    f = Foo()
    if hasattr(f, 'eat'):		# 判断有没有
        getattr(f, 'eat')()		# 获取并运行	# 'eat'
        
    f.count = 1
    print(f.__dict__)			# {'count': 1}
    setattr(f, 'eat', 222)		
    print(f.__dict__)			# {'count':1, 'eat': 222}
    
    print(getattr('eat'))		# 222
    
    setattr(f, 'count', 11)
    print(f.__dict__)			# {'count':11, 'eat':222}
    
    delattr(f, 'count')			# {'eat': 222}
    print(f.__dict__)
    
    • 可以用来写通用评论借口

    call

    class Foo:
        def __init__(self, name):
            self.name = name
            
        def __call__(self, *args, **kwargs):
            print('我触发了')
            
    f = Foo('leijun')
    f()		# 对象加括号出发call
    
    我触发了
    

    补充

    1.getattr

    • 厉害之处:除了对类反射,还能对文件(模块)反射。
    class A:
        def __init__(self):
            self.name = 'nick'
            # self.age='18'
    
        def method(self):
            print("method print")
    
    
    a = A()
    
    print(getattr(a, 'name',
                  'not find'))  # 如果a 对象中有属性name则打印self.name的值,否则打印'not find'
    print(getattr(a, 'age',
                  'not find'))  # 如果a 对象中有属性age则打印self.age的值,否则打印'not find'
    print(getattr(a, 'method', 'default'))  # 如果有方法method,否则打印其地址,否则打印default
    print(getattr(a, 'method', 'default')())  # 如果有方法method,运行函数并打印None否则打印default
    

    2.动态导入(importlib)

    • 注意:只能导到模块,不要导里面的东西,如果要使用内部的东西,使用getattr()

    例如:

    # 文件目录结构为
    |- import_lib
    	|= metaclass.py
    |- test.py
    
    
    # 这时如果要在test中动态导入metaclass	
    |- test.py
    import importlib
    
    # 绝对导入
    module_name = __import__('import_lib.metaclass')	# 这时解释器自己内部用的
    # module_name = importlib.import_module('import_lib.metaclass')  # 与上面的效果一致,官方建议使用这个
    
    # 相对导入
    module_name = importlib.import_module('.metaclass.py', 'import_lib')
    
    # 如果要用里面的东西
    res = getattr(module_name, xxx)
    

    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)  # 会调用Student的__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)
    print(s1)  # 会调用Student的__repr__方法
    

    总结:

    1. str函数或者print函数调用的是obj.__str__()
    2. repr函数或者交互式解释器调用的是obj.__repr__()

    注意:

    1. 如果__str__没有被定义,那么就会使用__repr__来代替输出。
    2. __str____repr__方法的返回值都必须是字符串。

    _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 = '张三'	# 可以对__slots__中的对象进行定义
    a1.age = 24
    # a1.hobby = '泡妞'  # AttributeError: 'A' object has no attribute 'hobby'
    print(a1.__slots__)	# ['name', 'age']
    

    注意事项:
    __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]赋值时,执行我')	# 注意是obj[key],不是obj.key
            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
    
    p1 = Person('张三', 24)
    

    输出:

    调用__new__,创建类实例
    调用__init__,初始化实例
    

    __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)
    
  • 相关阅读:
    Sketch 画原型比 Axure 好用吗?为什么?
    ps高级磨皮的7个步骤
    算法竞赛入门经典_第二章:循环结构程序设计_上机练习_MyAnswer
    文件操作 & 重定向
    阶乘之和 & 程序运行时间 & 算法分析
    《数据结构与算法分析:C语言描述_原书第二版》CH3表、栈和队列_reading notes
    TIJ——Chapter Two:Everything Is an Object
    LeetCode Reverse Linked List
    LeetCode Contains Duplicate
    LeetCode Contains Duplicate II
  • 原文地址:https://www.cnblogs.com/lucky75/p/11066827.html
Copyright © 2011-2022 走看看