5、绑定方法与非绑定方法
在类内部定义的函数,分为两大类:
一:绑定方法:绑定给谁,就应该由谁来调用,谁来调用就会把调用者当作第一个参数自动传入
绑定到对象的方法:在类内定义的没有被任何装饰器修饰的,会把自己当做第一个参数传进去;
绑定到类的方法:在类内定义的被装饰器classmethod修饰的方法。
二:非绑定方法:没有自动传值这么一说了,就类中定义的一个普通工具,对象和类都可以使用调用
非绑定方法:不与类或者对象绑定;
#####绑定到对象 class Foo: def __init__(self,name): self.name = name def tell(self): #绑定到对象的方法;类内部定义的函数就是给对象使用的 print('名字是%s'%self.name) f = Foo('egon') #print(Foo.tell) #<function Foo.tell at 0x000000000299CB70> 类访问自己内部函数属性的时候,它就是一个普通函数,没有自动传值一说 #Foo.tell(f) #名字是egon 类非要用,就要给它传值 # print(f.tell) #<bound method Foo.tell of <__main__.Foo object at 0x00000000027BF630>> 绑定方法指向类的那个函数,跟类使用的是一个功能。 f.tell() #名字是egon 绑定给谁就由谁来调用,就会把调用者当做第一个参数自动传进去;
#####绑定到类 class Foo: def __init__(self,name): self.name = name def tell(self): print('名字是%s'%self.name) @classmethod ###绑定到类的方法 def func(cls): #绑定到类,把类当做第一个参数传进去;cls=Foo print(cls) print(Foo.func) #<bound method Foo.func of <class '__main__.Foo'>> 加了装饰器后,定义成绑定到类的方法而不是函数了 Foo.func() #<class '__main__.Foo'>
######非绑定方法,没有自动传值了 class Foo: def __init__(self,name): self.name = name def tell(self): print('名字是%s'%self.name) @classmethod def func(cls): #绑定到类,把类当做第一个参数传进去; print(cls) @staticmethod #这才被认为一个真正意义上的函数,没有自动传值了。非绑定方法加个@staticmethod def func1(x,y): print(x+y) f = Foo('egon') f.func() #<class '__main__.Foo'> print(Foo.func1) #<function Foo.func1 at 0x000000000296CC80> 类 都是普通函数 print(f.func1) #<function Foo.func1 at 0x000000000296CC80> 对象 普通函数 Foo.func1(1,2) #3 类来调用 f.func1(2,4) #6 对象来调用
在类内部定义的函数分为两大类:绑定方法和非绑定方法(就是普通函数)。
使用场景
根据函数体的逻辑想传什么参数
class People: def __init__(self,name,age,sex): 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)) p = People('egon',18,'male') #绑定给对象,就应该由对象来调用,自动将对象本身当做第一个参数传入 p.tell_info() #tell_info(p) # Name:egon Age:18 Sex:male
import settings #写一个配置文件 name = 'alex' age = 18 sex = male class People: def __init__(self,name,age,sex): 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 #绑定给类,就应该由类来调用,自动将类本身当做第一个参数传入 p = People.from_conf() #本质上就是from_conf(People) #从配置文件里边读取配置进行实例化 p.tell_info() #Name:alex Age:18 Sex:male
import settings import hashlib import time class People: def __init__(self,name,age,sex): self.id = self.creat_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 creat_id(): #不依赖类和对象传入参数 m=hashlib.md5(str(time.time()).encode('utf-8')) return m.hexdigest() p1=People('egon1',18,'male') p2=People('egon2',28,'male') p3=People('egon3',38,'male') print(p1.create_id()) #66c0399486670e0faaab1324e98b33b0 print(p2.id) #66c0399486670e0faaab1324e98b33b0 print(p3.id) #66c0399486670e0faaab1324e98b33b0
6、反射
通过字符串来映射到一个对象的属性
class People: 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.name) #obj.__dict__['name'] #.后面是个属性,而不是字符串;用户通过字符串来映射到一个对象的属性身上 # print(obj.talk) #<bound method People.talk of <__main__.People object at 0x00000000027DFB00>> # choice=input('>>: ') #choice='name' # print(obj.choice) #会报错 print(obj.'name') #判断有没有这个属性 print(hasattr(obj,'name')) #True obj.name #obj.__dict__['name'] print(hasattr(obj,'talk')) #True obj.talk #拿到对象的属性 ,没有就报错 print(getattr(obj,'name')) # egon print(getattr(obj,'namexx',None)) # None
print(gatattr(obj,'talk')) #<bound method People.talk of <__main__.People object at 0x0000000002961F28>>
#修改 或新增 setattr(obj,'sex','male') #obj.sex = 'male' print(obj.sex) # male #删除 delattr(obj,'age') # del obj.age print(obj.__dict__) # {'name': 'egon', 'sex': 'male'} #类同样适用以上方法 print(getattr(People,'country')) #People.country #China
####反射的应用: class Service: def run(self): while True: inp=input('>>: ').strip() #cmd='get a.txt' cmds=inp.split() #cmds=['get','a.txt'] # print(cmds) if hasattr(self,cmds[0]): #判断有没有get这个方法 func=getattr(self,cmds[0]) #拿到它 func(cmds) #执行 def get(self,cmds): print('get.......',cmds) def put(self,cmds): print('put.......',cmds) obj=Service() obj.run()
7、内置方法
http://www.cnblogs.com/linhaifeng/articles/6204014.html
isinstance(obj,cls)检查是否obj是否是类 cls 的对象,判断谁是谁的实例;
issubclass(sub, super)检查sub类是否是 super 类的派生类
只要是__开头__结尾的方法都不要直接去用,python会自动调用的,什么情况下会触发呢?
####item系列 #把对象模拟成像字典样 ###获取 class Foo: #Dict def __init__(self,name): self.name=name def __getitem__(self, item): #要知道item获取的什么值 #item='name',把name传进去;我通过字符串怎么访问属性呢,可以用反射,也可以self.__dict__(item) print('getitem...') print(item) return self.__dict__.get(item) #这样就取到值了;通过字典的key取它的value ##有就取值,没有就不会报错了 def __setitem__(self, key, value): print('setitem...') print(key,value) self.__dict__[key]=value def __delitem__(self, key): print('delitem...') print(key) del self.__dict__[key] obj=Foo('egon') ##把obj模拟成字典的样子 print(obj.__dict__) #{'name': 'egon'} ###查看属性 #obj.属性名 #原来是这样获取的 #obj['name'] #打印出:getitem... name obj.name想要完成这样一个取值,应该让那个方法有个返回值;按照这种形式一打开它就会触发__getitem__来取值 #print(obj['name'] #打印出:getitem... name egon print(obj['namexx']) #打印出:getitem... namexx None obj.name
class Foo: #Dict def __init__(self,name): self.name=name def __getitem__(self, item): #要知道item获取的什么值 #item='name' print('getitem...') print(item) return self.__dict__.get(item) #通过字典的key取它的value #有就取值,没有就不会报错了 def __setitem__(self, key, value): #value就是要设置的值 #print('setitem...') #print(key,value) self.__dict__[key]=value #这样就完成了真正的设置,上边两步就可以去掉了 def __delitem__(self, key): print('delitem...') print(key) del self.__dict__[key] obj=Foo('egon') #print(obj.__dict__) #设置属性 #obj.sex = 'male' obj['sex'] = 'male' #会触发__setitem__,并且打印key,value print(obj.__dict__) #验证下,设置成功了 #{'name': 'egon', 'sex': 'male'} print(obj.sex) #获取 male
class Foo: #Dict def __init__(self,name): self.name=name def __getitem__(self, item): #要知道item获取的什么值 #item='name' #print('getitem...') #print(item) return self.__dict__.get(item) #通过字典的key取它的value #有就取值,没有就不会报错了 def __setitem__(self, key, value): #print('setitem...') #print(key,value) self.__dict__[key]=value #这样就完成了真正的设置,上边两步就可以去掉了 def __delitem__(self, key): #print('delitem...') #print(key) del self.__dict__[key] ##完成真正的删除 obj=Foo('egon') #print(obj.__dict__) #查看属性 #obj.属性名 #原来是这样获取的 #obj['name'] #obj.name 想要完成这样一个取值,应该让那个方法有个返回值;一打开它就会触发__getitem__来取值 #print(obj['namexx']) #obj.name #设置属性 #obj.sex = 'male' # obj['sex'] = 'male' #会触发__setitem__,并且打印key,value # print(obj.__dict__) #验证下,设置成功了 # print(obj.sex) #获取 #删除属性 #del obj.name del obj['name'] print(obj.__dict__) #delitem... name {}
#########__str__方法: # d=dict({'name':'egon'}) # print(isinstance(d,dict)) #True # print(d) #{'name','egon'} #打印成有用的东西,而不是打印成内存地址 class People: def __init__(self,name,age): self.name=name self.age=age def __str__(self): #__str__定义完之后,会在打印对象的时候会触发对象下的__str__方法,然后把返回字符串类型的结果打印出 print('====>str') return '<name:%s,age:%s>' %(self.name,self.age) #要返回一个字符串类型 obj=People('egon',18) print(obj) #打印出:====>str <name:egon,age:18> ;res=obj.__str__() #一打印的时候就会触发 obj.__str__()这个方法,把它的结果拿到打印
####__del__ 回收资源的 # f=open('settings.py') # f.read() # f.close() #回收操作系统的资源 ,执行它后f这个变量还存在 # print(f) #<_io.TextIOWrapper name='settings.py' mode='r' encoding='cp936'> # f.read() class Open: def __init__(self,filename): print('open file.......') #给操作系统发一个请求 self.filename=filename def __del__(self): ##在对象被删除的时候会先触发这个方法的执行再删除。 程序运行完之后py会自动给你删对象,在删对象之前先触发这个方法的执行; print('回收操作系统资源:self.close()') #py只会帮你回收对象本身,并不会给你回收对象相关的属性,这时候就可以写在这里了使用__del__ f=Open('settings.py') #它是一个变量,占应用程序的一个内存 #del f #del f 如果运行这一步是程序先运行打开文件,然后回收操作系统资源,最后执行完程序。 #同样也会触发它 f.__del__() print('----main------') ##代表程序执行完了会自动触发del f的执行; 但遗留下操作系统的资源。
8、元类介绍
exec:三个参数 参数一:字符串形式的命令 参数二:全局作用域(字典形式),如果不指定,默认为globals() 参数三:局部作用域(字典形式),如果不指定,默认为locals()
g={ 'x':1, 'y':2 } l={} #可以把exec看做一个函数,g,l是指定全局和局部作用域 exec(""" global x,m x=10 m=100 z=3 """,g,l) print(g) print(l)
一切皆对象,对象可以怎么用?
1、都可以被引用,x=obj
2、都可以当作函数的参数传入
3、都可以当作函数的返回值
4、都可以当作容器类的元素,l=[func,time,obj,1]
##类也是对象,Foo=type(....) 看做type传了个值进去 class Foo: pass obj=Foo() print(type(obj)) #<class '__main__.Foo'> print(type(Foo)) #<class 'type'> class Bar: pass print(type(Bar)) #查看类型 #<class 'type'> ##产生类的类称之为元类,默认所以用class定义的类,他们的元类是type
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为
元类的实例化的结果为我们用class定义的类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象.
定义类的两种方式:
#定义类的两种方式: #方式一:使用class关键字 class Chinese: #Chinese=type(...) country='China' def __init__(self,namem,age): self.name=namem self.age=age def talk(self): print('%s is talking' %self.name) # print(Chinese) obj=Chinese('egon',18) print(obj,obj.name,obj.age) #方式二:type 手动模拟class创建类的过程,将创建类的步骤拆分开,手动去创建。 #定义类的三要素:类名,类的基类们,类的名称空间 class_name='Chinese' #类名 class_bases=(object,) #继承的基类;类的父类 class_body=""" #类体 country='China' def __init__(self,namem,age): self.name=namem self.age=age def talk(self): print('%s is talking' %self.name) """ class_dic={} #放到字典里初始化为空 #应该把上边""" """里边类体的代码执行一下得到的结果放到一个字典里边; exec(class_body,globals(),class_dic) # print(class_dic) #{'country': 'China', '__init__': <function __init__ at 0x00000000003D3E18>, 'talk': <function talk at 0x0000000001F1CAE8>}
Chinese1=type(class_name,class_bases,class_dic) #得到一个元类 # print(Chinese1) #<class '__main__.Chinese'> obj1=Chinese1('egon',18) print(obj1,obj1.name,obj1.age) #<__main__.Chinese object at 0x00000000029619B0> egon 18
步骤一(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的名称空间),我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形),生成类的局部名称空间,即填充字典
步骤二:调用元类type(也可以自定义)来产生类Chinense
我们看到,type 接收三个参数:
- 第 1 个参数是字符串 ‘Foo’,表示类名
- 第 2 个参数是元组 (object, ),表示所有的父类
- 第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法
补充:若Foo类有继承,即class Foo(Bar):.... 则等同于type('Foo',(Bar,),{})
自定义元类控制类的创建
#一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的行为,工作流程是什么)
#自定义元类控制类的行为 class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): #可能覆盖父类;继承默认元类的一堆属性 if not class_name.istitle(): raise TypeError('类名的首字母必须大写') #会报错 print(class_dic) #{'__module__': '__main__', '__qualname__': 'Chinese', '__doc__': 'a\n 中文人的类 ##注释写上之后才有__doc__属性\n ', 'country': 'China', '__init__': <function Chinese.__init__ at 0x000000000296CB70>, 'talk': <function Chinese.talk at 0x000000000296CBF8>} if '__doc__' not in class_dic or not class_dic['__doc__'].strip(): raise TypeError('必须有注释,且注释不能为空') super(Mymeta,self).__init__(class_name,class_bases,class_dic) #你写__init__这个功能之前把父类的也重用了一遍 class Chinese(object,metaclass=Mymeta): #指定元类为Mymeta,这个类Chinese的创建行为就由你来控制了 '''a 中文人的类 ##注释写上之后才有__doc__属性 ''' country='China' def __init__(self,namem,age): self.name=namem self.age=age def talk(self): print('%s is talking' %self.name) # Chinese=Mymeta(class_name,class_bases,class_dic) #在实例化,必须要有个__init__方法;触发Mymeta实例化的过程就会触发__init__方法的执行
自定义元类控制类的实例化
#知识储备__call__方法 #让对象变成可调用对象 class Foo: def __call__(self, *args, **kwargs): print(self) #<__main__.Foo object at 0x0000000001DBB128> print(args) #(1, 2, 3) print(kwargs) #{'a': 1, 'b': 2, 'c': 3} obj=Foo() #obj之所以可以调用是因Foo里边有个__call__方法 obj(1,2,3,a=1,b=2,c=3) #obj.__call__(obj,1,2,3,a=1,b=2,c=3) # #元类内部也应有有一个__call__方法,会在调用Foo时触发该方法的执行 # #Foo(1,2,x=1) #Foo.__call__(Foo,1,2,x=1)
class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): if not class_name.istitle(): raise TypeError('类名的首字母必须大写') if '__doc__' not in class_dic or not class_dic['__doc__'].strip(): raise TypeError('必须有注释,且注释不能为空') super(Mymeta,self).__init__(class_name,class_bases,class_dic) def __call__(self, *args, **kwargs): #obj=Chinese('egon',age=18) #一个类实例化的行为就是__call__里边的内容 # print(self) #self=Chinese # print(args) #args=('egon',) # print(kwargs) #kwargs={'age': 18} #之前学的是调用Chinese所要做的三件事情: ##这其实就是__call__方法内部 #第一件事:先造一个空对象obj obj=object.__new__(self) #self= Chinese #第二件事:初始化obj self.__init__(obj,*args,**kwargs) #把空对象传进来,把参数传进来 #第三件事:返回obj return obj class Chinese(object,metaclass=Mymeta): ''' 中文人的类 ''' country='China' def __init__(self,namem,age): self.name=namem self.age=age def talk(self): print('%s is talking' %self.name) obj=Chinese('egon',age=18) #Chinese.__call__(Chinese,'egon',18) 调用这个对象会触发它的类Mymeta,下面那个__call__ print(obj.__dict__) #{'name': 'egon', 'age': 18}
应用
#单例模式 对象内部特征是一样的话就不要产生空间,用一个空间就可以了,节省空间 #实现方式一: class MySQL: __instance=None #__instance=obj1 def __init__(self): self.host='127.0.0.1' self.port=3306 @classmethod def singleton(cls): if not cls.__instance: obj=cls() cls.__instance=obj return cls.__instance def conn(self): pass def execute(self): pass # obj1=MySQL() # obj2=MySQL() # obj3=MySQL() # print(obj1) # print(obj2) # print(obj3) obj1=MySQL.singleton() obj2=MySQL.singleton() obj3=MySQL.singleton() print(obj1 is obj3)
#实现方式二:元类的方式 class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): if not class_name.istitle(): raise TypeError('类名的首字母必须大写') if '__doc__' not in class_dic or not class_dic['__doc__'].strip(): raise TypeError('必须有注释,且注释不能为空') super(Mymeta,self).__init__(class_name,class_bases,class_dic) self.__instance=None #设置个属性 def __call__(self, *args, **kwargs): #obj=Chinese('egon',age=18) if not self.__instance: #没有__instance就给它造一个 obj=object.__new__(self) #造一个空对象 self.__init__(obj) #self代表Mysql self.__instance=obj return self.__instance #有就返回 class Mysql(object,metaclass=Mymeta): ''' Mysql xxx ''' def __init__(self): self.host='127.0.0.1' self.port=3306 def conn(self): pass def execute(self): pass obj1=Mysql() #调用这个对象触发__call__下面的方法 obj2=Mysql() obj3=Mysql() print(obj1 is obj2 is obj3)
python中的__init__、__new__方法
class TestCls(): """docstring for TestCls""" def __init__(self, name): print('init') print(self) print(type(self)) self.name = name def __new__(cls, name): print('new') print(cls) print(type(cls)) return super().__new__(cls) c = TestCls("CooMark") # new... # <class '__main__.TestCls'> # <class 'type'> # init... # <__main__.TestCls object at 0x02201130> # <class '__main__.TestCls'>
异同点
- 参数
- __new__的第一个占位参数是class对象
- __init__的第一个占位参数是class的实例对象
- 其他的参数应一致
- 作用
- __new__ 用来创建实例,在返回的实例上执行__init__,如果不返回实例那么__init__将不会执行
- __init__ 用来初始化实例,设置属性什么的
__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass。
假如我们需要一个永远都是正数的整数类型,通过集成int,我们可能会写出这样的代码
class PositiveInteger(int): def __init__(self, value): super().__init__(self, abs(value)) i = PositiveInteger(-3) print(i) # # TypeError: object.__init__() takes no parameters class PositiveInteger(int): def __new__(cls, value): return super(PositiveInteger, cls).__new__(cls, abs(value)) i = PositiveInteger(-3) print(i) # 3
- 用__new__来实现单例
class Singleton(object): def __new__(cls): # 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象 if not hasattr(cls, 'instance'): cls.instance = super(Singleton, cls).__new__(cls) return cls.instance obj1 = Singleton() obj2 = Singleton() obj1.attr1 = 'value1' print( obj1.attr1, obj2.attr1) print( obj1 is obj2)
参考:http://www.jb51.net/article/48044.htm
__new__: 对象的创建,是一个静态方法,第一个参数是cls。(想想也是,不可能是self,对象还没创建,哪来的self)
__init__ : 对象的初始化, 是一个实例方法,第一个参数是self。
__call__ : 对象可call,注意不是类,是对象。
先有创建,才有初始化。即先__new__,而后__init__。
__call__方法 对象可call,注意不是类,是对象。
当实现了这个方法时,这个类的实例(对象)可以当作函数一样调用。
比如类A实现了__call__(self, x), 那么
a = A()
a(x) #把a当函数使
面向对象的软件开发
面向对象的软件工程包括下面几个部:
1.面向对象分析(object oriented analysis ,OOA)
软件工程中的系统分析阶段,要求分析员和用户结合在一起,对用户的需求做出精确的分析和明确的表述,从大的方面解析软件系统应该做什么,而不是怎么去做。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,从客观存在的事物和事物之间的关系,归纳出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,并将具有相同属性和行为的对象用一个类class来标识。
建立一个能反映这是工作情况的需求模型,此时的模型是粗略的。
2 面向对象设计(object oriented design,OOD)
根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计。
首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。然后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。
在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述
3 面向对象编程(object oriented programming,OOP)
根据面向对象设计的结果,选择一种计算机语言把它写成程序,可以是python
4 面向对象测试(object oriented test,OOT)
在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。
面向对的测试是用面向对象的方法进行测试,以类作为测试的基本单元。
5 面向对象维护(object oriendted soft maintenance,OOSM)
正如对任何产品都需要进行售后服务和维护一样,软件在使用时也会出现一些问题,或者软件商想改进软件的性能,这就需要修改程序。
由于使用了面向对象的方法开发程序,使用程序的维护比较容易。
因为对象的封装性,修改一个对象对其他的对象影响很小,利用面向对象的方法维护程序,大大提高了软件维护的效率,可扩展性高。
在面向对象方法中,最早发展的肯定是面向对象编程(OOP),那时OOA和OOD都还没有发展起来,因此程序设计者为了写出面向对象的程序,还必须深入到分析和设计领域,尤其是设计领域,那时的OOP实际上包含了现在的OOD和OOP两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。
现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工作不是由一个人从头到尾完成的,而是由不同的人分别完成,这样OOP阶段的任务就比较简单了。程序编写者只需要根据OOd提出的思路,用面向对象语言编写出程序既可。