面向对象高阶

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)

最新文章

  1. Nginx中文域名配置
  2. ueditor
  3. WPF,Silverlight与XAML读书笔记第四十三 - 多媒体支持之文本与文档
  4. 【知乎网】Linux IO 多路复用 是什么意思?
  5. POJ1815 Friendship(字典序最小最小割割边集)
  6. 在centos安装docker
  7. Codeforces Round #258 (Div. 2)(A,B,C,D)
  8. Android 的 ramdisk.img、system.img、userdata.img 作用说明,以及UBoot 系统启动过程
  9. PCB板的价格是怎么算出来的?
  10. JAVA之GUI编程ACTION事件
  11. AngularJS继续中
  12. jquery刷新iframe页面的方法(兼容主流)
  13. Hibernate之关联映射(一对多和多对一映射,多对多映射)
  14. java中的out of memory
  15. 使用二分查找判断某个数在某个区间中--如何判断某个IP地址所属的地区
  16. rsync+inotify实现实时同步案例【转】
  17. maven插件后报错:org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project
  18. STL中的容器作为返回值
  19. linux文件管理 文件搜索
  20. [leetcode]428. Serialize and Deserialize N-ary Tree序列化与反序列化N叉树

热门文章

  1. EXt js 学习笔记总结
  2. MySQL终端(Terminal)命令基本操作(转)
  3. Spring MVC-表单(Form)标签-下拉框(Dropdown)示例(转载实践)
  4. N天学习一个Linux命令之帮助命令:man
  5. sysman 账号 oracle em console的一些问题
  6. Linux(1):fork函数
  7. HDU 4534
  8. HDU 2563 统计问题(递推)
  9. UVA 10069 Distinct Subsequences(DP)
  10. oc65--协议应用1,接口.做数据类型限定