面向对象
类与函数:
def 函数名():逻辑 --> 函数在定义时不执行内部代码,调用时执行
class 类名:pass --> 类在定义时就已执行内部代码,生成名称空间(类名.__dict__),类名():实例化一个对象
类
country='大中国' #全局变量 class Chinese: country = '中国' #类变量,可以用类名.country(变量名)调用 def __init__(self,name): #实例变量 self.name=name print('-1-->',self.country) print('--2->',country) def play_ball(self,ball):#类方法,给实例调用 print('%s 正在打 %s' %(self.name,ball))
实例化
print(Chinese.__dict__) #以字典形式打印类的变量和方法 print('>xxx>',Chinese.country) #可用 Chinese.country='' 修改类变量
p1=Chinese('alex')
print(p1.__dict__) #以字典形式打印实例的变量和方法
print('实例--------》',p1.country)
p1.play_ball('篮球')
输出
{'__module__': '__main__', 'country': '中国', .......} >xxx> 中国 -1--> 中国 --2-> 大中国 {'name': 'aabb'} 实例--------》 中国 aabb 正在打 篮球
类变量是字典/列表时的修改
class Chinese: country='China' l=['a','b'] d={'a':1} def __init__(self,name): self.name=name p1=Chinese('aaa') print(p1.l) p1.l=[1,2,3] print(Chinese.l) print(p1.__dict__) print('********') p1.l.append('c') p1.d['c']=3 print(p1.__dict__) print(Chinese.l) print(Chinese.d) 》》》: ['a', 'b'] ['a', 'b'] {'name': 'aaa', 'l': [1, 2, 3]} ******** {'name': 'aaa', 'l': [1, 2, 3, 'c']} ['a', 'b'] {'a': 1, 'c': 3}
class Stu: pass def func(self): print("func:%s" % self.age) s1 = Stu() s1.func = func s1.age = 12 print(s1.__dict__) # ================== a = s1.func(self=s1)
静态属性、静态方法、类方法
在类内部定义的函数,分为两大类:
一:绑定方法:绑定给谁,就应该由谁来调用,谁来调用就回把调用者当作第一个参数自动传入
绑定到对象的方法:在类内定义的没有被任何装饰器修饰的方法
绑定到类的方法:在类内定义的被装饰器classmethod修饰的方法
二:非绑定方法: staticmethod
没有自动传值这么一说了,就类中定义的一个普通工具,对象和类都可以使用
非绑定方法:不与类或者对象绑定
class Room: tag=1 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @property def cal_area(self): # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length)) return self.width * self.length r1=Room('A','a',1,2,3) print(r1.cal_area) #把方法当属性调用,看似是静态属性,其实可以有内部逻辑
class Room: tag=1 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @classmethod #类方法,可以用类名直接调用而不借助实例 def tell_info(cls,x): print(cls) print('--》',cls.tag,x)#print('--》',Room.tag) Room.tell_info(2)
class Room: tag=1 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @staticmethod #类的工具包,类和实例都能调用它,但它不能调用类变量和实例变量 def wash_body(a,b,c): print('%s %s %s正在洗澡' %(a,b,c)) Room.wash_body(1,3,2) r1=Room('A','a',1,2,3) r1.wash_body(1,3,2)
类方法(@classmethod,绑定到类的方法):类和实例都能调用,能访问类变量和实例变量
普通方法(没有装饰器,绑定到对象的方法):实例调用,能访问类变量和实例变量
静态方法(@staticmethod,普通的函数):类和实例都能调用,不能访问类变量和实例变量
import hashlib import time import settings # 保存name、age、sex的配置文件 class People: def __init__(self,name,age,sex): self.id=self.create_id() self.name=name self.age=age self.sex=sex def tell_info(self): #绑定到对象的方法 print('Name:%s Age:%s Sex:%s' %(self.name,self.age,self.sex)) @classmethod def from_conf(cls): obj=cls( settings.name, settings.age, settings.sex ) return obj @staticmethod def create_id(): m=hashlib.md5(str(time.time()).encode('utf-8')) return m.hexdigest() # p=People('egon',18,'male') #绑定给对象,就应该由对象来调用,自动将对象本身当作第一个参数传入 # p.tell_info() #tell_info(p) #绑定给类,就应该由类来调用,自动将类本身当作第一个参数传入 # p=People.from_conf() #from_conf(People) # p.tell_info() #非绑定方法,不与类或者对象绑定,谁都可以调用,没有自动传值一说 p1=People('egon1',18,'male') p2=People('egon2',28,'male') p3=People('egon3',38,'male') print(p1.id) print(p2.id) print(p3.id)
组合
用于类和类之间做关联
class Hand: pass class Foot: pass class Trunk: pass class Head: pass class Person: def __init__(self,id_num,name): self.id_num=id_num self.name=name self.hand=Hand() self.foot=Foot() self.trunk=Trunk() self.head=Head()
class School: def __init__(self,name,addr): self.name=name self.addr=addr def zhao_sheng(self): print('%s 正在招生' %self.name) class Course: def __init__(self,name,price,period,school): self.name=name self.price=price self.period=period self.school=school s1=School('oldboy','北京') s2=School('oldboy','南京') s3=School('oldboy','东京') msg=''' 1 老男孩 北京校区 2 老男孩 南京校区 3 老男孩 东京校区 ''' while True: print(msg) menu={ '1':s1, '2':s2, '3':s3 } choice=input('选择学校>>: ') school_obj=menu[choice] name=input('课程名>>: ') price=input('课程费用>>: ') period=input('课程周期>>: ') new_course=Course(name,price,period,school_obj) new_course.school.zhao_sheng() print('课程【%s】属于【%s】学校' %(new_course.name,new_course.school.name))
接口继承
父类定义接口函数但不定义具体功能,子类继承时实现其功能且必须实现
import abc class All_file(metaclass=abc.ABCMeta): #装饰器对子类进行限制,如果子类没有具体实现父类的方法,则报错 @abc.abstractmethod def read(self): pass @abc.abstractmethod def write(self): pass class Disk(All_file): def read(self): print('disk read') def write(self): print('disk write') class Cdrom(All_file): def read(self): print('cdrom read') def write(self): print('cdrom write') class Mem(All_file): def read(self): print('mem read') def write(self): print('mem write') m1=Mem() m1.read() m1.write()
多继承:python3默认广度优先
class A(): def __init__(self): print('A') class B(A): #def __init__(self): # print('B') pass class C(A): def __init__(self): print('C') class D(B,C): pass #python3默认广度优先,B,C都没有构造函数init才继承A #py2 经典类按深度优先继承,新式类按广度优先 a = D()
继承
class Vehicle1: Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦') class Subway(Vehicle1): def __init__(self,name,speed,load,power,line): # Vehicle.__init__(self,name,speed,load,power) # super().__init__(name,speed,load,power) #super(__class__,self).__init__(name,speed,load,power) super(Subway,self).__init__(name,speed,load,power)#继承父类变量 self.line=line #子类独有变量 def show_info(self): print(self.name,self.speed,self.load,self.power,self.line) def run(self): # Vehicle.run(self) super().run() print('%s %s 线,开动啦' %(self.name,self.line)) line13=Subway('北京地铁','10km/s',1000000000,'电',13) line13.show_info() line13.run() print(line13.__class__)
多态
多态性是指在不考虑实例类型的情况下使用实例
不同的对象调用相同的方法
多态在继承中实现
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' class H2O: def __init__(self,name,temperature): self.name=name self.temperature=temperature def turn_ice(self): if self.temperature < 0: print('[%s]温度太低结冰了' %self.name) elif self.temperature > 0 and self.temperature < 100: print('[%s]液化成水' %self.name) elif self.temperature > 100: print('[%s]温度太高变成了水蒸气' %self.name) def aaaaaaaaaaaaaa(self): pass class Water(H2O): pass class Ice(H2O): pass class Steam(H2O): pass w1=Water('水',25) i1=Ice('冰',-20) s1=Steam('蒸汽',3000) # w1.turn_ice() # i1.turn_ice() # s1.turn_ice() def func(obj): obj.turn_ice() func(w1) #---->w1.turn_ice() func(i1) #---->i1.turn_ice() # def func(obj): # obj.turn_ice() # # func(w1) # func(i1) # func(s1)
封装
class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #此时我们想求的是面积 return self.__width * self.__length *self.__high def tell_width(self): return self.__width r1=Room('卫生间','alex',100,100,10000) r1._Room__width=1 print(r1._Room__width) # arear=r1.__width * r1.__length print(r1.tell_area())
双下划线开头的变量为私有属性,外部不能直接访问,因为python内部对其重命名,外部访问时需要用(_类名变量名)
单例模式
1、__new__
class Singleton(object): _instance = None def __new__(cls, *args, **kw): if not cls._instance: cls._instance = super(Singleton, cls).__new__(cls, *args, **kw) return cls._instance class MyClass(Singleton): # 正常写自己的类 pass one = MyClass()
2、利用模块
定义好类之后直接实例化一个对象,要使用这个类时,直接调用该对象
class My_Singleton(object): x = 12 def foo(self): print(self.x) my_singleton = My_Singleton()
from mysingleton import my_singleton a = my_singleton # 引用实例
其它
运算符重载
print(1+2) print('1'+'2') class Person(): def __init__(self,num): self.num = num #运算符重载 def __add__(self, other):#打印地址 return Person(self.num + other.num) def __str__(self):#打印数字 return 'num ='+ str(self.num) per1 = Person(1) per2 = Person(2) #print (per1+per2) TypeError: unsupported operand type(s) for +: 'Person' and 'Person' print (per1+per2)# == print (per1.__add__(per2)) print (per1.__add__(per2)) print(per1) print (per2)
重写: __str()__ 和 __repr()__
'''重写 __str__() : 在print对象名时自动调用,给用户用的 是一个描述对象的方法 __repr__(): 是给机器用的,在Python解释器里面直接输对象名 在回车后调用的方法 在没有str但有repr时,repr==str ''' class Dog(): def __init__(self,name,age,height,sex): self.name = name self.age = age self.height = height self.sex = sex def __str__(self): return '---str---' dog = Dog('WangCai',5,15,'M') print(dog) #显示 def __str__
元类
1、type
>>> def fn(self, name='world'): # 先定义函数 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class '__main__.Hello'>
要创建一个class对象,type()函数依次传入3个参数:
class的名称;
继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
2、metaclass
#知识储备: #产生的新对象 = object.__new__(继承object类的子类) #步骤一:如果说People=type(类名,类的父类们,类的名称空间),那么我们定义元类如下,来控制类的创建 class Mymeta(type): # 继承默认元类的一堆属性 def __init__(self, class_name, class_bases, class_dic): if '__doc__' not in class_dic or not class_dic.get('__doc__').strip(): raise TypeError('必须为类指定文档注释') if not class_name.istitle(): raise TypeError('类名首字母必须大写') super(Mymeta, self).__init__(class_name, class_bases, class_dic) class People(object, metaclass=Mymeta): country = 'China' def __init__(self, name, age): self.name = name self.age = age def talk(self): print('%s is talking' % self.name) #步骤二:如果我们想控制类实例化的行为,那么需要先储备知识__call__方法的使用 class People(object,metaclass=type): def __init__(self,name,age): self.name=name self.age=age def __call__(self, *args, **kwargs): print(self,args,kwargs) # 调用类People,并不会出发__call__ obj=People('egon',18) # 调用对象obj(1,2,3,a=1,b=2,c=3),才会出发对象的绑定方法obj.__call__(1,2,3,a=1,b=2,c=3) obj(1,2,3,a=1,b=2,c=3) #打印:<__main__.People object at 0x10076dd30> (1, 2, 3) {'a': 1, 'b': 2, 'c': 3} #总结:如果说类People是元类type的实例,那么在元类type内肯定也有一个__call__,会在调用People('egon',18)时触发执行,然后返回一个初始化好了的对象obj #步骤三:自定义元类,控制类的调用(即实例化)的过程 class Mymeta(type): #继承默认元类的一堆属性 def __init__(self,class_name,class_bases,class_dic): if not class_name.istitle(): raise TypeError('类名首字母必须大写') super(Mymeta,self).__init__(class_name,class_bases,class_dic) def __call__(self, *args, **kwargs): #self=People print(self,args,kwargs) #<class '__main__.People'> ('egon', 18) {} #1、实例化People,产生空对象obj obj=object.__new__(self) #2、调用People下的函数__init__,初始化obj self.__init__(obj,*args,**kwargs) #3、返回初始化好了的obj return obj class People(object,metaclass=Mymeta): country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name) obj=People('egon',18) print(obj.__dict__) #{'name': 'egon', 'age': 18} #步骤四: class Mymeta(type): #继承默认元类的一堆属性 def __init__(self,class_name,class_bases,class_dic): if not class_name.istitle(): raise TypeError('类名首字母必须大写') super(Mymeta,self).__init__(class_name,class_bases,class_dic) def __call__(self, *args, **kwargs): #self=People print(self,args,kwargs) #<class '__main__.People'> ('egon', 18) {} #1、调用self,即People下的函数__new__,在该函数内完成:1、产生空对象obj 2、初始化 3、返回obj obj=self.__new__(self,*args,**kwargs) #2、一定记得返回obj,因为实例化People(...)取得就是__call__的返回值 return obj class People(object,metaclass=Mymeta): country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name) def __new__(cls, *args, **kwargs): obj=object.__new__(cls) cls.__init__(obj,*args,**kwargs) return obj obj=People('egon',18) print(obj.__dict__) #{'name': 'egon', 'age': 18} #步骤五:基于元类实现单例模式,比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存 class Mysql: __instance=None def __init__(self,host='127.0.0.1',port='3306'): self.host=host self.port=port @classmethod def singleton(cls,*args,**kwargs): if not cls.__instance: cls.__instance=cls(*args,**kwargs) return cls.__instance obj1=Mysql() obj2=Mysql() print(obj1 is obj2) #False obj3=Mysql.singleton() obj4=Mysql.singleton() print(obj3 is obj4) #True #应用:定制元类实现单例模式 class Mymeta(type): def __init__(self,name,bases,dic): #定义类Mysql时就触发 self.__instance=None super().__init__(name,bases,dic) def __call__(self, *args, **kwargs): #Mysql(...)时触发 if not self.__instance: self.__instance=object.__new__(self) #产生对象 self.__init__(self.__instance,*args,**kwargs) #初始化对象 #上述两步可以合成下面一步 # self.__instance=super().__call__(*args,**kwargs) return self.__instance class Mysql(metaclass=Mymeta): def __init__(self,host='127.0.0.1',port='3306'): self.host=host self.port=port obj1=Mysql() obj2=Mysql() print(obj1 is obj2)
练习:
class Mymetaclass(type): def __new__(cls,name,bases,attrs): update_attrs={} for k,v in attrs.items(): if not callable(v) and not k.startswith('__'): update_attrs[k.upper()]=v else: update_attrs[k]=v return type.__new__(cls,name,bases,update_attrs) class Chinese(metaclass=Mymetaclass): country='China' tag='Legend of the Dragon' #龙的传人 def walk(self): print('%s is walking' %self.name) print(Chinese.__dict__) ''' {'__module__': '__main__', 'COUNTRY': 'China', 'TAG': 'Legend of the Dragon', 'walk': <function Chinese.walk at 0x0000000001E7B950>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None} '''
1.元类帮其完成创建对象,以及初始化操作; 2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument 3.key作为用户自定义类产生对象的属性,且所有属性变成大写 class Mymetaclass(type): # def __new__(cls,name,bases,attrs): # update_attrs={} # for k,v in attrs.items(): # if not callable(v) and not k.startswith('__'): # update_attrs[k.upper()]=v # else: # update_attrs[k]=v # return type.__new__(cls,name,bases,update_attrs) def __call__(self, *args, **kwargs): if args: raise TypeError('must use keyword argument for key function') obj = object.__new__(self) #创建对象,self为类Foo for k,v in kwargs.items(): obj.__dict__[k.upper()]=v return obj class Chinese(metaclass=Mymetaclass): country='China' tag='Legend of the Dragon' #龙的传人 def walk(self): print('%s is walking' %self.name) p=Chinese(name='egon',age=18,sex='male') print(p.__dict__)
内置方法:
1、isinstance(obj,cls)检查是否obj是否是类 cls 的对象
issubclass(sub, super)检查sub类是否是 super 类的派生类
2、反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
class BlackMedium: feature='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_house(self): print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name) def rent_house(self): print('%s 黑中介租房子啦,傻逼才租呢' %self.name) b1=BlackMedium('万成置地','回龙观天露园') #检测是否含有某属性 print(hasattr(b1,'name')) print(hasattr(b1,'sell_house')) #获取属性 n=getattr(b1,'name') print(n) func=getattr(b1,'rent_house') func() # getattr(b1,'aaaaaaaa') #报错 print(getattr(b1,'aaaaaaaa','不存在啊')) #设置属性 setattr(b1,'sb',True) setattr(b1,'show_name',lambda self:self.name+'sb') print(b1.__dict__) print(b1.show_name(b1)) #删除属性 delattr(b1,'addr') delattr(b1,'show_name') delattr(b1,'show_name111')#不存在,则报错 print(b1.__dict__)
import sys def s1(): print 's1' def s2(): print 's2' this_module = sys.modules[__name__] hasattr(this_module, 's1') getattr(this_module, 's2')
3、__setattr__,__delattr__,__getattr__
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 self.__dict__.pop(item) #__setattr__添加/修改属性会触发它的执行 f1=Foo(10) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z=3 print(f1.__dict__) #__delattr__删除属性的时候会触发 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx
4、对内置方法的二次加工
class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid def append(self, p_object): ' 派生自己的append:加上类型检查' if not isinstance(p_object,int): raise TypeError('must be int') super().append(p_object) @property def mid(self): '新增自己的属性' index=len(self)//2 return self[index] l=List([1,2,3,4]) print(l) l.append(5) print(l) # l.append('1111111') #报错,必须为int类型 print(l.mid) #其余的方法都继承list的 l.insert(0,-123) print(l) l.clear() print(l)
5、__getattribute__
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('执行的是我') # return self.__dict__[item] f1=Foo(10) print(f1.x) f1.xxxxxx #不存在的属性访问,触发__getattr__
class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print('不管是否存在,我都会执行') f1=Foo(10) f1.x f1.xxxxxx
#当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('执行的是我') # return self.__dict__[item] def __getattribute__(self, item): print('不管是否存在,我都会执行') raise AttributeError('哈哈') f1=Foo(10) f1.x f1.xxxxxx
6、描述符(__get__,__set__,__delete__)
描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议 。__get__():调用一个属性时,触发。 __set__():为一个属性赋值时,触发。 __delete__():采用del删除属性时,触发
# 描述符Str class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') # 描述符Int class Int: def __get__(self, instance, owner): print('Int调用') def __set__(self, instance, value): print('Int设置...') def __delete__(self, instance): print('Int删除...') class People: name = Str() age = Int() def __init__(self, name, age): # name被Str类代理,age被Int类代理, self.name = name self.age = age # 何地?:定义成另外一个类的类属性 # 何时?:且看下列演示 p1 = People('alex', 18) # >>>:Str设置... # >>>:Int设置... # 描述符Str的使用 p1.name # >>>:Str调用 p1.name = 'egon' # >>>:Str设置.. # del p1.name # >>>:Str删除.. # 描述符Int的使用 p1.age p1.age = 18 del p1.age # 我们来瞅瞅到底发生了什么 print(p1.__dict__) print(People.__dict__) # # # 补充 print(type(p1) == People) # type(obj)其实是查看obj是由哪个类实例化来的 print(type(p1).__dict__ == People.__dict__)
基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
7、再看property
一个静态属性property本质就是实现了get,set,delete三种方法
class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self, value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') # 只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1 = Foo() f1.AAA # >>: get的时候运行我啊 f1.AAA = 'aaa' # >>: set的时候运行我啊 del f1.AAA # >>: delete的时候运行我啊
class Foo: def get_AAA(self): print('get的时候运行我啊') def set_AAA(self): print('set的时候运行我啊') def delete_AAA(self): print('delete的时候运行我啊') AAA = property(get_AAA, set_AAA, delete_AAA) # 内置property三个参数与get,set,delete一一对应 f1 = Foo() f1.AAA f1.AAA = 'aaa' del f1.AAA
class Goods: def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): print("修改价格") self.original_price = value @price.deleter def price(self): print("删除价格") del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价
# 实现类型检测功能 # 第一关: class People: def __init__(self, name): self.name = name @property def name(self): return self.name # p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常 # 第二关:修订版 class People: def __init__(self, name): self.name = name # 实例化就触发property @property def name(self): # return self.name #无限递归 print('get------>') return self.DouNiWan @name.setter def name(self, value): print('set------>') self.DouNiWan = value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1 = People('alex') # self.name实际是存放到self.DouNiWan里 print(p1.name) print(p1.name) print(p1.name) print(p1.__dict__) p1.name = 'egon' print(p1.__dict__) del p1.name print(p1.__dict__) # 第三关:加上类型检查 class People: def __init__(self, name): self.name = name # 实例化就触发property @property def name(self): # return self.name #无限递归 print('get------>') return self.DouNiWan @name.setter def name(self, value): print('set------>') if not isinstance(value, str): raise TypeError('必须是字符串类型') self.DouNiWan = value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1 = People('alex') # self.name实际是存放到self.DouNiWan里 p1.name = 1
8、slots
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象, 也可以是一个字符串(意味着所有实例只有一个数据属性) 2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典 (类的字典是共享的,而每个实例的是独立的) 3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类, 但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__ 当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。 实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个 字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到 这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给 实例添加新的属性了,只能使用在__slots__中定义的那些属性名。 4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外, 定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。 大多数情况下,你应该 只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中 需要创建某个类的几百万个实例对象 。 关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。 尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。 更多的是用来作为一个内存优化工具。
在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象。
class Foo: __slots__=['name','age'] f1=Foo() f1.name='alex' f1.age=18 print(f1.__slots__) f2=Foo() f2.name='egon' f2.age=19 print(f2.__slots__) print(Foo.__dict__) #f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存