类的继承、多态、封装

一、类的继承

1、应用场景:

类大部分功能相同,大类包含小类的情况

例如:

动物类

共性:都要吃喝拉撒、都有头有脚

特性:

猫类、走了很轻,叫声特别,喜欢白天睡觉

狗类、的叫声很大,晚上睡觉

2、继承顺序

在python2版本中多重继承有分两种继承循序

(1)、一直往上找 、找到最高级的父类再重另外一个分支找,直到找到为止。

(2)、一直往上找,找到最高级父类的下一层后就不找了。从另外一个分支找,另外一个分支没找到最后才找最高级的父类。

再python3中只有第一种继承循序。

3、子类调用父类的方法:

4、接口继承和归一化设计

接口继承和归一化设计例子:

  1. import abc
  2.  
  3. class Animal(metaclass=abc.ABCMeta):
  4. @abc.abstractclassmethod
  5. def shout(self): #定义抽象方法,无需实现功能
  6. pass
  7.  
  8. @abc.abstractclassmethod
  9. def run(self):
  10. pass
  11.  
  12. @abc.abstractclassmethod
  13. def jump(self):
  14. pass
  15.  
  16. class Felid(Animal):
  17. # 子类继承抽象类,但是必须定义shout、run、jump方法,否则实例化后就报错了。这个就是归一化设计。
  18. def __init__(self,name):
  19. self.name = name
  20.  
  21. def shout(self):
  22. print("%s 在叫" % self.name)
  23.  
  24. def run(self):
  25. print("%s 在跑" % self.name)
  26.  
  27. def jump(self):
  28. print("%s 在跳" % self.name)
  29.  
  30. tiger = Felid("老虎")
  31. tiger.shout()

接口继承和归一化设计

二、类的多态

1、应用场景:

不同的类有相同的方法,不同的对象调用相同的方法。多态是依附于继承存在的,有了继承才能有多态。

例如:

H2O分子:有三种形态体现

(1)、水

(2)、冰

(3)、水蒸气

2、代码实现例子

  1. class H2O:
  2. def __init__(self,name,temp):
  3. self.name = name
  4. self.temp=temp
  5.  
  6. def who_am_i(self):
  7. if int(self.temp) <0:
  8. print("I am %s." % self.name)
  9. elif int(self.temp) >0 and int(self.temp) <100:
  10. print("I am %s" % self.name)
  11. else:
  12. print("I am %s" % self.name)
  13.  
  14. class Ice(H2O):
  15. pass
  16.  
  17. class Water(H2O):
  18. pass
  19.  
  20. class Steam(H2O):
  21. pass
  22.  
  23. s1 = Ice("冰",-1)
  24. s2 = Water("水",10)
  25. s3 = Steam("空气",1000)
  26.  
  27. def func(obj):
  28. obj.who_am_i()
  29.  
  30. func(s1)
  31. func(s2)
  32. func(s3)

多态的例子

三、类的封装

1、单下划线和双下划线的私有属性和方法

2、反射

实现反射的四个方法:

hasattr(object,name),getattr(object,name,default=None),setattr(object,name,value),delattr(object,name)

四种方法例子展示:

  1. class Foo:
  2. foo_name = "my foo test"
  3.  
  4. def __init__(self,name,addr):
  5. self.name = name
  6. self.addr = addr
  7.  
  8. def test1(self):
  9. print("test1")
  10.  
  11. def test2(self):
  12. print("test2")
  13.  
  14. # hasattr
  15. t1 = Foo("FooTest","beijing")
  16. print(hasattr(t1,"foo_name"))
  17. print(hasattr(t1,"test1"))
  18. # getattr
  19. print(getattr(t1,"foo_name"))
  20. func1 = getattr(t1,"test1")
  21. func1()
  22. print(getattr(t1,"test10","not found"))
  23. # setattr
  24. setattr(t1,"start_time","")
  25. print(t1.__dict__)
  26. #delattr
  27. delattr(t1,"start_time")
  28. print(t1.__dict__)
  29.  
  30. #打印显示##
  31. True
  32. True
  33. my foo test
  34. test1
  35. not found
  36. {'name': 'FooTest', 'addr': 'beijing', 'start_time': ''}
  37. {'name': 'FooTest', 'addr': 'beijing'}

反射四个方法使用例子

好处:

(1)、实现可插拔机制

  1. # a同学写的类
  2. class FtpClient:
  3. "A同学写的功能,但是没有完成"
  4. def __init__(self,addr):
  5. print("正在连接服务器%s " % addr)
  6. self.addr = addr
  7.  
  8. # b同学要调用,并进行其他的工作
  9. f1 = FtpClient("192.168.1.1")
  10. if hasattr(f1,"get"):
  11. func_get = getattr(f1,"get")
  12. func_get
  13. else:
  14. print("功能还未完成,等待中。。")

反射插拔式设计例子

可以看出,一方没有完成的,完全不影响另一方的其他工作的进行。

(2)、实现动态导入模块

  1. import importlib
  2.  
  3. __import__("import_lib.mataclass")
  4. #importlib.import_module("import_lib.mataclass")
  5. #两种效果一样,但是官方建议用下面一种。

3、__getattr__,__setattr__,delattr__

例子:

  1. class Foo:
  2. x =1
  3. def __init__(self,y):
  4. self.y = y
  5.  
  6. def __getattr__(self, item):
  7. print("----> from getattr: 你找的属性不存在")
  8.  
  9. def __setattr__(self, key, value):
  10. print("----> from setattr: ")
  11. # self.key = value # 这里这么写是一个无限递归
  12. self.__dict__[key]=value # __setattr__ 其实就是重写了__dict__,所以不是特殊需求,一般都不会去动这块。
  13.  
  14. def __delattr__(self, item):
  15. print("----> from delattr: ")
  16. self.__dict__.pop(item)
  17.  
  18. # 触发__getattr__
  19. f1 = Foo(10)
  20. f1.z # 只有在使用点调用属性且属性找不到的时候才触发__getattr__
  21.  
  22. # 触发__setattr__
  23. f1.z = 3
  24. print(f1.__dict__) # 这里显示字典为空,是因为重写了__setattr__,凡是赋值操作都会触发他的运行,这里什么都没有写,所以是空
  25. # 触发__delattr__
  26. f1.a = 3#
  27. del f1.a
  28. print(f1.__dict__)

演示例子

4、二次加工标准类型

(1)、包装

包装:python为我们提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了继承、派生知识。

例子:

  1. class List(list):
  2. def append(self, p_object):
  3. "派生自己的append方法:加上类型检测"
  4. if not isinstance(p_object,int):
  5. print("must be int")
  6. return
  7. super().append(p_object)
  8.  
  9. def mid(self):
  10. "新增一个取列表中间值的方法"
  11. index = len(self)//2
  12. return self[index]
  13. # 只允许数值进行append操作
  14. L1 = List([1,2,3,4,5])
  15. print(L1)
  16. L1.append(10)
  17. print(L1)
  18. L1.append("")
  19. print(L1)
  20. # 取中间值
  21. print(L1.mid())
  22.  
  23. ##打印信息
  24. [1, 2, 3, 4, 5]
  25. [1, 2, 3, 4, 5, 10]
  26. must be int
  27. [1, 2, 3, 4, 5, 10]
  28. 4

包装例子

(2)、授权

授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其他的则保持原样。授权的过程,即是所有更新的功能都是有心累的某部分来处理,但是已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法。

例子:

  1. import time
  2.  
  3. class FileHandle:
  4. def __init__(self,filename,mode,encoding):
  5. self.file=open(filename,mode=mode,encoding=encoding)
  6.  
  7. def write(self,line): #重写了write方法
  8. t = time.strftime("%Y-%m-%d %X")
  9. self.file.write("%s %s" % (t,line))
  10.  
  11. def __getattr__(self, item):
  12. # print(self.file,item,type(item))
  13. return getattr(self.file,item)
  14.  
  15. f1 = FileHandle("a.txt","w+","utf-8")
  16. f1.write("aaaaaaaa\n")
  17. f1.write("bbbb\n")
  18. f1.seek(0) ##使用原来open类的seek方法
  19. print(f1.read())
  20.  
  21. ##打印
  22. 2017-05-04 14:58:48 aaaaaaaa
  23. 2017-05-04 14:58:48 bbbb

授权例子

5、__setitem__,__getitem__,delitem__

这三个方法和上面的__setattr__,__getattr__,delattr__差不多,区别就在于,attr是用点的方式去访问的,item的是用["key"]的方式访问的。

例子:

  1. class Foo:
  2. def __init__(self,name):
  3. self.name = name
  4.  
  5. def __getitem__(self, item):
  6. print("----> getitem: ")
  7. print(self.__dict__[item])
  8.  
  9. def __setitem__(self, key, value):
  10. print("----> setitem: ")
  11. self.__dict__[key] = value
  12.  
  13. def __delitem__(self, key):
  14. print("----> delitem: ")
  15. self.__dict__.pop(key)
  16.  
  17. def __delattr__(self, item):
  18. print("----> delattr: ")
  19. self.__dict__.pop(item)
  20.  
  21. f1 = Foo("aaa")
  22. f1['age'] = 18 # 触发setitem
  23. f1['name'] #触发 getitem
  24. print(f1.__dict__)
  25. del f1.name
  26. del f1['age']
  27. f1["name"] = "bbb"
  28. print(f1.__dict__)
  29.  
  30. #打印
  31. ----> setitem:
  32. ----> getitem:
  33. aaa
  34. {'name': 'aaa', 'age': 18}
  35. ----> delattr:
  36. ----> delitem:
  37. ----> setitem:
  38. {'name': 'bbb'}

例子

6、__str__,__repr__,__format__

改变对象的字符串显示__str__,__repr__

自定制格式化字符串__format__

例子:

  1. format_dict = {
  2. 'nat':'{obj.name}-{obj.addr}-{obj.type}',
  3. 'tna':'{obj.type}:{obj.name}:{obj.addr}',
  4. 'tan':'{obj.type}/{obj.addr}/{obj.name}',
  5. }
  6.  
  7. class School:
  8. def __init__(self,name,addr,type):
  9. self.name = name
  10. self.addr = addr
  11. self.type = type
  12.  
  13. def __repr__(self):
  14. return 'School(%s,%s)' % (self.name,self.addr)
  15.  
  16. def __str__(self):
  17. return "(%s,%s)" %(self.name,self.addr)
  18.  
  19. def __format__(self, format_spec):
  20. if not format_spec or format_spec not in format_dict:
  21. format_spec='nat'
  22. fmt = format_dict[format_spec]
  23. return fmt.format(obj=self)
  24.  
  25. s1 = School("清华","beijing","public")
  26. print('from repr: ',repr(s1))
  27. print('from str: ',str(s1))
  28. print(s1)
  29. """
  30. str函数或者print函数--->obj.__str__()
  31. repr函数或者交互式解释器--->obj.__repr__()
  32. 如果__str__没有定义,那么就会使用__repr__来代替输出
  33. 注意:这两个方法的返回值必须是字符串,否则抛出异常
  34. """
  35. print(format(s1,'nat'))
  36. print(format(s1,'tna'))
  37. print(format(s1,'tan'))
  38. print(format(s1,'aaa'))
  39.  
  40. ##打印
  41. from repr: School(清华,beijing)
  42. from str: (清华,beijing)
  43. (清华,beijing)
  44. 清华-beijing-public
  45. public:清华:beijing
  46. public/beijing/清华
  47. 清华-beijing-public

例子

7、__slots__

最大的用途就是用来节省内存。

例子:

  1. '''
  2. 1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
  3. 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
  4. 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
  5. 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
  6. 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
  7. 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
  8. 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
  9. 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
  10. 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。
  11.  
  12. '''
  13. class Foo:
  14. __slots__='x'
  15.  
  16. f1=Foo()
  17. f1.x=1
  18. f1.y=2#报错
  19. print(f1.__slots__) #f1不再有__dict__
  20.  
  21. class Bar:
  22. __slots__=['x','y']
  23.  
  24. n=Bar()
  25. n.x,n.y=1,2
  26. n.z=3#报错
  27.  
  28. __slots__使用

例子

8、__iter__,__next__

例子:

  1. class Fib:
  2. def __init__(self):
  3. self._a = 0
  4. self._b = 1
  5.  
  6. def __iter__(self):
  7. return self
  8.  
  9. def __next__(self):
  10. self._a,self._b=self._b,self._a + self._b
  11. return self._a
  12.  
  13. f1 = Fib()
  14.  
  15. print(f1.__next__())
  16. print(next(f1))
  17. print(next(f1))
  18.  
  19. for i in f1:
  20. if i > 100:
  21. break
  22. print('%s ' %i,end='')
  23.  
  24. #打印
  25. 1
  26. 1
  27. 2
  28. 3 5 8 13 21 34 55 89

实现迭代器协议例子

9、文件描述符__set__,__get__,__delete__

(1)、描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议

__get__():调用一个属性时,触发

__set__():为一个属性赋值时,触发

__delete__():采用del删除属性时,触发

(2)、描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的属性,不能定义到构造函数中)

(3)、描述符分两种:数据描述符(至少实现了__get__()和__set__())、非数据描述符(没有实现__set__())

(4)、注意事项:

(4.1)、描述符本身应该定义成新式类,被代理的类也应该是新式类

(4.2)、必须把描述符定义成这个类的类属性,不能定义在构造函数中

(4.3)、要严格遵循该优先级,优先级由高到低分别是:

(4.3.1)、类属性

(4.3.2)、数据描述符

(4.3.3)、实例属性

(4.3.4)、非数据描述符

(4.3.5)、找不到的属性触发__getattr__()

(5)、描述符的使用

  1. class Str:
  2. "数据描述符"
  3. def __get__(self, instance, owner):
  4. print("-->instance: %s" % instance)
  5. print("-->owner: %s" % owner)
  6.  
  7. def __set__(self, instance, value):
  8. print("-->instance: %s" % instance)
  9. print("-->value: %s" % value)
  10.  
  11. def __delete__(self, instance):
  12. print("-->instance: %s" % instance)
  13.  
  14. class People:
  15. name = Str() #描述符
  16. def __init__(self,name,age,salary):
  17. self.name = name
  18. self.age = age
  19. self.salary = salary
  20.  
  21. p1 = People("test1",22,1121.1) # 触发 __set__
  22. p1.name # 触发__get__
  23. print(p1.__dict__) #发现name属性没有。这里说明一个问题,是数据描述符优先级高于实例属性。
  24.  
  25. ##打印
  26. -->instance: <__main__.People object at 0x000002CA13189978>
  27. -->value: test1
  28. -->instance: <__main__.People object at 0x000002CA13189978>
  29. -->owner: <class '__main__.People'>
  30. {'age': 22, 'salary': 1121.1}

数据描述符优先级例子

  1. class Str:
  2. "数据描述符"
  3. def __init__(self,key):
  4. self.key = key
  5.  
  6. def __get__(self, instance, owner):
  7. return instance.__dict__[self.key]
  8.  
  9. def __set__(self, instance, value):
  10. if not isinstance(value,str):
  11. print("传入是数据不是字符串!")
  12. # raise TypeError("传入是数据不是字符串!")
  13. return
  14. instance.__dict__[self.key] = value
  15.  
  16. def __delete__(self, instance):
  17. instance.__dict__.pop[self.key]
  18.  
  19. class People:
  20. name = Str("name") #数据描述符
  21. def __init__(self,name,age,salary):
  22. self.name = name
  23. self.age = age
  24. self.salary = salary
  25.  
  26. p1 = People("test1",22,1121.1) # 触发 __set__
  27. p1.name # 触发__get__
  28. print(p1.__dict__) #发现name属性没有。这里说明一个问题,是数据描述符优先级高于实例属性。
  29. p2 = People(1111,22,232323)
  30. print(p2.__dict__)
  31.  
  32. #打印
  33. {'name': 'test1', 'age': 22, 'salary': 1121.1}
  34. 传入是数据不是字符串!
  35. {'age': 22, 'salary': 232323}

赋值类型判断的描述符例子

  1. class Str:
  2. "数据描述符"
  3. def __init__(self,key,exspect_type):
  4. self.key = key
  5. self.exspect_type = exspect_type
  6.  
  7. def __get__(self, instance, owner):
  8. return instance.__dict__[self.key]
  9.  
  10. def __set__(self, instance, value):
  11. if not isinstance(value,self.exspect_type):
  12. print("传入是数据不是%s" % self.exspect_type)
  13. # raise TypeError("传入是数据不是字符串!")
  14. return
  15. instance.__dict__[self.key] = value
  16.  
  17. def __delete__(self, instance):
  18. instance.__dict__.pop[self.key]
  19.  
  20. class People:
  21. name = Str("name",str) #数据描述符
  22. age = Str("name",int) #数据描述符
  23. salary = Str("name",float) #数据描述符
  24. def __init__(self,name,age,salary):
  25. self.name = name
  26. self.age = age
  27. self.salary = salary
  28.  
  29. p1 = People("test1",22,1121.1) # 触发 __set__
  30. p1.name # 触发__get__
  31. print(p1.__dict__) #发现name属性没有。这里说明一个问题,是数据描述符优先级高于实例属性。
  32. p2 = People("test2",22,232323)
  33. print(p2.__dict__)
  34.  
  35. #打印
  36. {'name': 1121.1}
  37. 传入是数据不是<class 'float'>
  38. {'name': 22}

支持多类型判断的描述符例子

(6)、类的装饰器

  1. def Typed(**kwargs):
  2. def decorate(obj):
  3. for k,v in kwargs.items():
  4. setattr(obj,k,v)
  5. return obj
  6. return decorate
  7.  
  8. @Typed(x=1,y=2,z=3)
  9. class Foo:
  10. pass
  11. print(Foo.__dict__)
  12. print(Foo.x)
  13.  
  14. @Typed(name = "test1")
  15. class Bar:
  16. pass
  17. print(Bar.name)
  18.  
  19. #打印
  20. {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3}
  21. 1
  22. test1

类的装饰器(带参数)

  1. class Typed:
  2. def __init__(self,name,expect_type):
  3. self.name = name
  4. self.expect_type = expect_type
  5.  
  6. def __get__(self, instance, owner):
  7. return instance.__dict__[self.name]
  8.  
  9. def __set__(self, instance, value):
  10. if not isinstance(value,self.expect_type):
  11. raise TypeError("类型错误,不是%s" % self.expect_type) # 接收到实例化的值并判断传进来的值的类型是否是装饰器传进来的数据类型。条件满足就赋值。
  12. instance.__dict__[self.name] = value
  13.  
  14. def __delete__(self, instance):
  15. instance.__dict__.pop(self.name)
  16.  
  17. def func(**kwargs):
  18. def decorate(obj):
  19. for k,v in kwargs.items():
  20. print(k,v)
  21. setattr(obj,k,Typed(k,v)) #重点在这里,这里将k,v传到Typed类中,也就是Typed类的两个属性,name和expect_type。
  22. return obj
  23. return decorate
  24.  
  25. @func(name=str,age=int) #(2)在实例化之前有一个func装饰器,装饰器会将Foo类传入func。@dacorate ==》Foo=dacorate(Foo)
  26. class Foo:
  27. def __init__(self,name,age):
  28. self.name = name
  29. self.age = age
  30.  
  31. f1 = Foo("test1",19) #(1)实例化的时候将值传给Foo类,
  32. print(f1.__dict__)
  33.  
  34. @func(salary=float)
  35. class Bar:
  36. def __init__(self,salary):
  37. self.salary = salary
  38.  
  39. b1 = Bar(1000.0)
  40. print(b1.__dict__)

装饰器+数据描述符来限定数据传入的类型判断

(7)、自定制property

  1. class Newproperty:
  2. def __init__(self,func):
  3. self.func = func
  4.  
  5. def __get__(self, instance, owner):
  6. return self.func(instance)
  7.  
  8. class Foo:
  9. def __init__(self,name,width,length):
  10. self.name = name
  11. self.width = width
  12. self.length = length
  13.  
  14. @Newproperty # 等同于 area = Newproperty(area)
  15. def area(self):
  16. return self.width * self.length
  17.  
  18. f1 = Foo("房子",10,14)
  19. print(f1.area)
  20. print(f1.__dict__)
  21.  
  22. #打印
  23. 140
  24. {'name': '房子', 'width': 10, 'length': 14}

定制property例子

  1. class Lazyproperty:
  2. def __init__(self,func):
  3. self.func = func
  4.  
  5. def __get__(self, instance, owner):
  6. value = self.func(instance)
  7. setattr(instance,self.func.__name__,value) # 这里将值保存到了对象的字典里面。所以当值存在的时候就不会在重复赋值了。
  8. return value
  9. # def __set__(self, instance, value): # 如果这里加了set,那么优先级就不一样了。属于数据描述符的优先级了。所以这里的延迟计算就失效了。
  10. # print("aaaa")
  11.  
  12. class Foo:
  13. def __init__(self,name,width,length):
  14. self.name = name
  15. self.width = width
  16. self.length = length
  17.  
  18. @Lazyproperty # 等同于 area = Newproperty(area)
  19. def area(self):
  20. return self.width * self.length
  21.  
  22. f1 = Foo("房子",10,14)
  23. print(f1.area)
  24. print(f1.__dict__)

实现延迟计算功能

(8)、描述符总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

描述符是很多高级哭和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件。

10、__init__,__del__

构造函数,析构函数

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

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

构造函数__init__:用于初始化类的内容部状态,也就是说当该类实例化的时候就会执行该函数。

11、__enter__,__exit__

上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

  1. class Open:
  2. def __init__(self,name):
  3. self.name = name
  4.  
  5. def __enter__(self):
  6. print("出现with语句,对象的__enter__被触发,有返回值这复制给as声明的变量")
  7.  
  8. def __exit__(self, exc_type, exc_val, exc_tb):
  9. print("with 中代码执行完毕执行这里。")
  10. print(exc_type)
  11. print(exc_val)
  12. print(exc_tb)
  13. return True #如果少了这句的话,抛出异常后就不执行下面的代码了
  14.  
  15. with Open('a.txt') as f:
  16. print("======>执行代码快")
  17. raise AttributeError("抛出异常")
  18. print(""*100)

例子

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

如果__exit__()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后面的语句正常执行

好处:

(1)、使用with语句的目的就是把代码快放入with中执行,with结束后,自动完成清理工作,无须手动干预

(2)、在需要管理一些资源,比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无需再去关心这个问题。后面的网络编程将大有用处。

12、property

13、__doc__

这个方法是类的描述信息

特点:这个属性无法被继承

14、__module__和__class__

__module__表示当前操作的对象在哪个模块

__class__表示当前操作的对象的类是什么

15、__call__

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

注:构造方法的执行是由创建对象触发的,即:对象 = 类名;而对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()

16、metaclass,元类

python中一切皆对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象

创建类的两种方式:

(1):

  1. class Foo:
  2. def func(self):
  3. print('from func')

(2):

  1. def func(self):
  2. print('from func')
  3. x=1
  4. Foo=type('Foo',(object,),{'func':func,'x':1})

什么是元类:

元类是类的类,是类的模版

元类是用来控制如何创建类的,正如类是创建对象的模版一样。

元类的实例为类,正如类的实例对象(f1对象是Foo类的一个实例,Foo类是type类的一个实例。)

type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象。

  1. class Mytype(type):
  2. def __init__(self,what,bases=None,dict=None):
  3. print('mytype init')
  4.  
  5. def __call__(self, *args, **kwargs):
  6. obj=self.__new__(self)
  7. self.__init__(obj,*args,**kwargs)
  8. return obj
  9.  
  10. class Foo(object,metaclass=Mytype):
  11. x=1111111111
  12.  
  13. def __init__(self,name):
  14. self.name=name
  15.  
  16. def __new__(cls, *args, **kwargs):
  17. return super().__new__(cls)
  18.  
  19. f1=Foo('egon')
  20.  
  21. print(f1.__dict__)
  22.  
  23. 自定制元类纯净版

自制元类例子

  1. #元类总结
  2. class Mymeta(type):
  3. def __init__(self,name,bases,dic):
  4. print('===>Mymeta.__init__')
  5.  
  6. def __new__(cls, *args, **kwargs):
  7. print('===>Mymeta.__new__')
  8. return type.__new__(cls,*args,**kwargs)
  9.  
  10. def __call__(self, *args, **kwargs):
  11. print('aaa')
  12. obj=self.__new__(self)
  13. self.__init__(self,*args,**kwargs)
  14. return obj
  15.  
  16. class Foo(object,metaclass=Mymeta):
  17. def __init__(self,name):
  18. self.name=name
  19. def __new__(cls, *args, **kwargs):
  20. return object.__new__(cls)
  21.  
  22. '''
  23. 需要记住一点:名字加括号的本质(即,任何name()的形式),都是先找到name的爹,然后执行:爹.__call__
  24.  
  25. 而爹.__call__一般做两件事:
  26. 1.调用name.__new__方法并返回一个对象
  27. 2.进而调用name.__init__方法对儿子name进行初始化
  28. '''
  29.  
  30. '''
  31. class 定义Foo,并指定元类为Mymeta,这就相当于要用Mymeta创建一个新的对象Foo,于是相当于执行
  32. Foo=Mymeta('foo',(...),{...})
  33. 因此我们可以看到,只定义class就会有如下执行效果
  34. ===>Mymeta.__new__
  35. ===>Mymeta.__init__
  36. 实际上class Foo(metaclass=Mymeta)是触发了Foo=Mymeta('Foo',(...),{...})操作,
  37. 遇到了名字加括号的形式,即Mymeta(...),于是就去找Mymeta的爹type,然后执行type.__call__(...)方法
  38. 于是触发Mymeta.__new__方法得到一个具体的对象,然后触发Mymeta.__init__方法对对象进行初始化
  39. '''
  40.  
  41. '''
  42. obj=Foo('egon')
  43. 的原理同上
  44. '''
  45.  
  46. '''
  47. 总结:元类的难点在于执行顺序很绕,其实我们只需要记住两点就可以了
  48. 1.谁后面跟括号,就从谁的爹中找__call__方法执行
  49. type->Mymeta->Foo->obj
  50. Mymeta()触发type.__call__
  51. Foo()触发Mymeta.__call__
  52. obj()触发Foo.__call__
  53. 2.__call__内按先后顺序依次调用儿子的__new__和__init__方法
  54. '''

python类的相关知识第二部分的更多相关文章

  1. python类的相关知识第一部分

    一.类的相关概念 (1).什么是类 具有同种属性的对象称为类,是个抽象的概念.比如说:汽车.人.狗.神: (2).什么是对象或实例 日常生活中的所有东西都是对象,是类的实例化.比如说:推土车是汽车的实 ...

  2. Python面对对象相关知识总结

    很有一段时间没使用python了,前两天研究微信公众号使用了下python的django服务,感觉好多知识都遗忘了,毕竟之前没有深入的实践,长期不使用就忘得快.本博的主要目的就是对Python中我认为 ...

  3. Python中元组相关知识

    下面给大家介绍以下元组的相关知识: ·元组可以看成是一个不可更改的list 1.元组的创建 # 创建空元祖 t = () print(type(t)) # 创建只有一个值的元组 # 观察可知元组中如果 ...

  4. python 类编程相关内容(更新)

    python作为面向对象的编程语言,类和对象相关的编程当然是少不了的! python类: class 类名 : 变量名 [ = 初始值 ] …… def 函数名 ( self [ , 其余参数列表 ] ...

  5. Python 网络编程相关知识学习

    Python 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的 ...

  6. python之类的相关知识

    面向对象技术简介 类: 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.对象是类的实例. 类变量:类变量在整个实例化的对象中是公用的.类变量定义在类中且在函数体之 ...

  7. 深入理解ES6之——JS类的相关知识

    基本的类声明 类声明以class关键字开始,其后是类的名称:剩余部分的语法看起来像对象字面量中的方法简写,并且在方法之间不需要使用逗号. class Person { //等价于prototype的构 ...

  8. python 列表list相关知识

    List的元素可以是Python的任意数据类型(Boolean,Number,String,List,Dict,Set……) List同样可以使用索引和切片,切片得到的结果也是列表. print(li ...

  9. Java常用类Date相关知识

    Date:类 Date 表示特定的瞬间,精确到毫秒. 在 JDK 1.1 之前,类 Date 有两个其他的函数.它允许把日期解释为年.月.日.小时.分钟和秒值.它也允许格式化和解析日期字符串. Dat ...

随机推荐

  1. codility上的问题(34) Fluorum 2014

    好久没写codility的题了.一来没时间,二来有的题目不太好分析.这个题比較有意思,我还没有给出很严格的证明.

  2. unity文件写入与读取

    using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; us ...

  3. Sublime Text 2 入门与总结

    Sublime Text 2 入门与总结   首语 :    考完试,但又没什么兴趣做课程设计,蛋疼的弄点软件入门的介绍,希望给各位还在吃香蕉的程序猿带来一点启示...   代码编辑器,就像武侠中的武 ...

  4. JQuery EasyUI datagrid pageNumber 分页 请求/加载 两次

    解决方案: 原因是 jquery.easyui.min.js 源文件中,由于第1页的total和其他页的total不相等,EasyUI会重新发起第1页的请求!1.jQuery EasyUI 1.4.1 ...

  5. nodejs系列笔记01---Buffer

    纯JavaScript无法处理二进制数据,buffer就是用来处理二进制数据的 原始数据保存在buffer实例中,一个buffer实例类似于数组.buffer的大小在建立时指定的不可更改. buffe ...

  6. EasyUI的window加载的页面不执行js问题说明

    http://364434006.iteye.com/blog/1671907 ———————————————————————————————————————————————————————————— ...

  7. Tomcat nginx log日志按天分割切割

    利用 Linux 自带的 logrotate 工具来实现按天切割日志.下方已 centos 7 系统为例来实践讲解. 原理 Logrotate是基于CRON来运行的,其脚本是/etc/cron.dai ...

  8. JFinal使用笔记3-注册和登录功能开发记录

    首页 开源项目 问答 代码 博客 翻译 资讯 移动开发 招聘 城市圈 当前访客身份:游客 [ 登录 | 加入开源中国 ]   当前访客身份: 游客 [ 登录 | 加入开源中国 ] 软件   土龙 关注 ...

  9. mysql 8 安装及更改密码

    一 下载ZIP版的安装文件, 二 解压缩至指定的目录,如:d:\mysql8.0 三 在所在目录下,新建mysql.ini文件 [mysql] # 设置mysql客户端默认字符集 default-ch ...

  10. json中的日期格式转换(扩展new date()显示格式)

    在java  spring mvc 开发过程中,通过json 格式,向前端传递数据,日期格式发生了转变, 在前台数据展示时,要进行一定格式的转换才能正常显示: 我在开发中使用了easy ui 和my ...