参考:面向对象程序设计 http://www.cnblogs.com/linhaifeng/articles/6182264.html
面向对象高级 http://www.cnblogs.com/linhaifeng/articles/6204014.html
1. 函数作用域实现类的功能
用函数的作用域也可实现类的功能,如下:
def dog(name,gender,type): # 狗的动作 def jiao(dog): print('一条狗[%s],汪汪汪' % dog['name']) def chi_shi(dog): print('一条[%s] 正在吃屎' % dog['type']) def init(name,gender,type): dog1 = { 'name':name, 'gender': gender, 'type': type, 'jiao':jiao, 'chi_shi':chi_shi, } return dog1 return init(name,gender,type) d1=dog('元昊','母','中华田园犬') #调用函数dog()的时候会返回init()函数的调用结果.init()返回值中包括了 狗 的数据和动作. d2=dog('alex','母','藏敖') print(d1) print(d2) d1['jiao'](d1) #调用函数jiao,参数是它自己(class中的self) d2['chi_shi'](d2)
2. 面向对象一些概念
面向对象设计: 将一类事物的数据和动作整合到一起。
面向对象编程: object-oriented programming, 用定义类 + 实例/对象的方式去实现面向对象的设计.
类: 把一类事物相同的特征和动作整合到一起就是类,类是一个抽象的概念
对象: 基于类创建的一个具体的事物(具体存在的。)
类与对象的关系:对象都是由类产生。类是对象的模板或蓝图,类是对象的抽象化,对象/实例是类的实例化。类不代表具体的事物,而对象表示具体的事物。
实例化:由类生产对象的过程叫做实例化,类实例化的结果就是对象,或者叫做一个实例。(实例=对象)
工厂函数: 当作一个类,生产对象.
3. 类与对象
再现实世界先有对象再有类。
在程序中: 先定义类,后产生对象

#在程序中,务必保证:先定义(类),后使用(产生对象) PS: 1. 在程序中特征用变量标识,技能用函数标识 2. 因而类中最常见的无非是:变量和函数的定义 #程序中的类 class OldboyStudent: school='oldboy' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') #注意: 1.类中可以有任意python代码,这些代码在类定义阶段便会执行 2.因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过OldboyStudent.__dict__查看 3.对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法 4.点是访问属性的语法,类中定义的名字,都是类的属性 #程序中类的用法 .:专门用来访问属性,本质操作的就是__dict__ OldboyStudent.school #等于经典类的操作OldboyStudent.__dict__['school'] OldboyStudent.school='Oldboy' #等于经典类的操作OldboyStudent.__dict__['school']='Oldboy' OldboyStudent.x=1 #等于经典类的操作OldboyStudent.__dict__['x']=1 del OldboyStudent.x #等于经典类的操作OldboyStudent.__dict__.pop('x') #程序中的对象 #调用类,或称为实例化,得到对象 s1=OldboyStudent() s2=OldboyStudent() s3=OldboyStudent() #如此,s1、s2、s3都一样了,而这三者除了相似的属性之外还各种不同的属性,这就用到了__init__ #注意:该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值 class OldboyStudent: ...... def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex ...... s1=OldboyStudent('李坦克','男',18) #先调用类产生空对象s1,然后调用OldboyStudent.__init__(s1,'李坦克','男',18) s2=OldboyStudent('王大炮','女',38) s3=OldboyStudent('牛榴弹','男',78) #程序中对象的用法 #执行__init__,s1.name='牛榴弹',很明显也会产生对象的名称空间 s2.__dict__ {'name': '王大炮', 'age': '女', 'sex': 38} s2.name #s2.__dict__['name'] s2.name='王三炮' #s2.__dict__['name']='王三炮' s2.course='python' #s2.__dict__['course']='python' del s2.course #s2.__dict__.pop('course') 在程序中:先定义类,后产生对象
#python为类内置的特殊属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中) 类的特殊属性(了解即可)

#1、在没有学习类这个概念时,数据与功能是分离的 def exc1(host,port,db,charset): conn=connect(host,port,db,charset) conn.execute(sql) return xxx def exc2(host,port,db,charset,proc_name) conn=connect(host,port,db,charset) conn.call_proc(sql) return xxx #每次调用都需要重复传入一堆参数 exc1('127.0.0.1',3306,'db1','utf8','select * from tb1;') exc2('127.0.0.1',3306,'db1','utf8','存储过程的名字') #2、我们能想到的解决方法是,把这些变量都定义成全局变量 HOST=‘127.0.0.1’ PORT=3306 DB=‘db1’ CHARSET=‘utf8’ def exc1(host,port,db,charset): conn=connect(host,port,db,charset) conn.execute(sql) return xxx def exc2(host,port,db,charset,proc_name) conn=connect(host,port,db,charset) conn.call_proc(sql) return xxx exc1(HOST,PORT,DB,CHARSET,'select * from tb1;') exc2(HOST,PORT,DB,CHARSET,'存储过程的名字') #3、但是2的解决方法也是有问题的,按照2的思路,我们将会定义一大堆全局变量,这些全局变量并没有做任何区分,即能够被所有功能使用,然而事实上只有HOST,PORT,DB,CHARSET是给exc1和exc2这两个功能用的。言外之意:我们必须找出一种能够将数据与操作数据的方法组合到一起的解决方法,这就是我们说的类了 class MySQLHandler: def __init__(self,host,port,db,charset='utf8'): self.host=host self.port=port self.db=db self.charset=charset def exc1(self,sql): conn=connect(self.host,self.port,self.db,self.charset) res=conn.execute(sql) return res def exc2(self,sql): conn=connect(self.host,self.port,self.db,self.charset) res=conn.call_proc(sql) return res obj=MySQLHandler('127.0.0.1',3306,'db1') obj.exc1('select * from tb1;') obj.exc2('存储过程的名字') #改进 class MySQLHandler: def __init__(self,host,port,db,charset='utf8'): self.host=host self.port=port self.db=db self.charset=charset self.conn=connect(self.host,self.port,self.db,self.charset) def exc1(self,sql): return self.conn.execute(sql) def exc2(self,sql): return self.conn.call_proc(sql) obj=MySQLHandler('127.0.0.1',3306,'db1') obj.exc1('select * from tb1;') obj.exc2('存储过程的名字') 数据与专门操作该数据的功能组合到一起
类即类型
提示:python的class术语与c++有一定区别,与 Modula-3更像。
python中一切皆为对象,且python3中类与类型是一个概念,类型就是类
#类型dict就是类dict >>> list <class 'list'> #实例化的到3个对象l1,l2,l3 >>> l1=list() >>> l2=list() >>> l3=list() #三个对象都有绑定方法append,是相同的功能,但内存地址不同 >>> l1.append <built-in method append of list object at 0x10b482b48> >>> l2.append <built-in method append of list object at 0x10b482b88> >>> l3.append <built-in method append of list object at 0x10b482bc8> #操作绑定方法l1.append(3),就是在往l1添加3,绝对不会将3添加到l2或l3 >>> l1.append(3) >>> l1 [3] >>> l2 [] >>> l3 [] #调用类list.append(l3,111)等同于l3.append(111) >>> list.append(l3,111) #l3.append(111) >>> l3 [111]
类方法和静态方法
参考:https://www.cnblogs.com/wangyongsong/p/6750454.html
https://blog.csdn.net/youngbit007/article/details/68957848
@classmethod 是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法。 类方法的第一个参数cls,而实例方法的第一个参数是self,表示该类的一个实例。
普通对象方法至少需要一个self参数,代表类对象实例
类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。
类方法,可以通过类来调用,就像C.f(cls,args)
classmethod必须使用类对象作为第一个参数,而staticmethod则可以不传递任何参数。
classmethod可以访问到类的属性和方法(cls参数),但是不能访问到实例的属性和方法。
staticmethod即不能访问到实例的属性也不能访问到类的属性
强调,注意注意注意:静态方法和类方法虽然是给类准备的,但是如果实例去用,也是可以用的,只不过实例去调用的时候容易让人混淆,不知道你要干啥
class Date: def __init__(self,day=0, month=0, year=0): self.day=day self.month = month self.year = year @classmethod #至少会由以个参数cls def from_string(cls, date_as_string): # 第一个参数是cls day, month, year = map(int,date_as_string.split('-')) my_date = cls(day, month, year) return my_date @staticmethod #可以没有参数 def is_date_valid(date_as_string): day, month, year = map(int, date_as_string.split('-')) return day <= 31 and month <= 12 and year <= 3999 if __name__ == '__main__': my_date = Date.from_string('11-09-2012') print(my_date.day, my_date.month,my_date.year) is_date = Date.is_date_valid('13-13-2012') print(is_date) outputs: 11 9 2012 False
为了验证有子类继承时,子类调用该类方法时,传入的类变量cls是子类,而非父类
class A: @classmethod def cm(cls): print('类方法cm(cls)调用者:', cls.__name__) @staticmethod def sm(): print('静态方法sm()被调用') class B(A): pass A.cm() # 类方法cm(cls)调用者: A B.cm() # 类方法cm(cls)调用者: B A.sm() # 静态方法sm()被调用 B.sm() # 静态方法sm()被调用
为什么要用到staticmethod与classmethod。
class Kls: def __init__(self,data): self.data = data def printd(self): print(self.data) ik1=Kls('arun') ik2=Kls('seema') ik1.printd() ik2.printd() # 如果现在我们想写一些仅仅与类交互而不是和实例交互的方法会怎么样呢? 我们可以在类外面写一个简单的方法来做这些, # 但是这样做就扩散了类代码的关系到类定义的外面. 如果像下面这样写就会导致以后代码维护的困难: def get_no_of_instances(cls_obj): return cls_obj.no_inst class Kls: no_inst = 0 def __init__(self): Kls.no_inst = Kls.no_inst + 1 ik1 = Kls() ik2 = Kls() print(get_no_of_instances(Kls)) # 2
应用classmethod
class Kls(object): no_inst = 0 def __init__(self): Kls.no_inst = Kls.no_inst + 1 @classmethod def get_no_of_instance(cls_obj): return cls_obj.no_inst ik1 = Kls() ik2 = Kls() print(ik1.get_no_of_instance()) print(Kls.get_no_of_instance()) # 2 # 2
应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了
class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 @staticmethod def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 t=time.localtime(time.time()+86400) return Date(t.tm_year,t.tm_mon,t.tm_mday) a=Date('1987',11,27) #自己定义时间 b=Date.now() #采用当前时间 c=Date.tomorrow() #采用明天的时间 print(a.year,a.month,a.day) print(b.year,b.month,b.day) print(c.year,c.month,c.day)
classmethod应用场景:

类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python为我们内置了函数classmethod来把类中的函数定义成类方法 import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): t=time.localtime() return Date(t.tm_year,t.tm_mon,t.tm_mday) class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,但是结果为 ''' 输出结果: <__main__.Date object at 0x1013f9d68> ''' 因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day # @staticmethod # def now(): # t=time.localtime() # return Date(t.tm_year,t.tm_mon,t.tm_mday) @classmethod #改成类方法 def now(cls): t=time.localtime() return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化 class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿 ''' 输出结果: year:2017 month:3 day:3 '''
附加知识点 __str__
#__str__定义在类内部,必须返回一个字符串类型, #什么时候会出发它的执行呢?打印由这个类产生的对象时,会触发执行 class People: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return '<name:%s,age:%s>' %(self.name,self.age) p1=People('egon',18) print(p1) str(p1) #----->p1.__str__()
4. 属性查找
类有两种属性:数据属性和函数属性
1. 类的数据属性是所有对象共享的
2. 类的函数属性是绑定给对象用的
class OldboyStudent: school='oldboy' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') #类的数据属性是所有对象共享的,id都一样 print(id(OldboyStudent.school)) print(id(s1.school)) print(id(s2.school)) print(id(s3.school)) ''' 4377347328 4377347328 ''' #类的函数属性是绑定给对象使用的,obj.method称为绑定方法,内存地址都不一样 #ps:id是python的实现机制,并不能真实反映内存地址,如果有内存地址,还是以内存地址为准 print(OldboyStudent.learn) print(s1.learn) print(s2.learn) print(s3.learn) ''' <function OldboyStudent.learn at 0x1021329d8> <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x1021466d8>> <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x102146710>> <bound method OldboyStudent.learn of <__main__.OldboyStudent object at 0x102146748>> '''
我们定义的类属性到底寸到哪里去了,2个方法查看
dir(类名) : 查出的是名字列表,包含了内置的属性
类名.__dict__ :查处的是一个字典,key是属性名,value是属性值. OldboyStudent.__dict__['learn']
实例属性和类属性
:一般用类去访问类属性,不推荐用实例去访问类属性。类属性的更改会影响到所有实例
>>> class T(object): ... versin = 0.1 ... def test(self): ... print "this is a test!" ... ... ... >>> T.versin 0.1 >>> t = T() >>> t.versin 0.1 >>> T.versin=0.2 >>> T.versin #改变类属性的值 0.2
4.2 静态属性-属性函数(property)
参考: http://python.jobbole.com/80955/
使用属性函数的最简单的方法之一是将它作为一个方法的装饰器来使用。这可以让你将一个类方法转变成一个类属性。class Person(object): """"""
#---------------------------------------------------------------------- def __init__(self, first_name, last_name): """Constructor""" self.first_name = first_name self.last_name = last_name #---------------------------------------------------------------------- @property def full_name(self): """ Return the full name """ return "%s %s" % (self.first_name, self.last_name)
>>> person = Person("Mike", "Driscoll")
使用Python property取代setter和getter方法
让我们假设我们有一些遗留代码,它们是由一些对Python理解得不够好的人写的。如果你像我一样,你之前已经看到过这类的代码:
from decimal import Decimal ######################################################################## class Fees(object): """""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" self._fee = None #---------------------------------------------------------------------- def get_fee(self): """ Return the current fee """ return self._fee #---------------------------------------------------------------------- def set_fee(self, value): """ Set the fee """ if isinstance(value, str): self._fee = Decimal(value) elif isinstance(value, Decimal): self._fee = value
要使用这个类,我们必须要使用定义的getter和setter方法:
如果你想添加可以使用正常点符号访问的属性,而不破坏所有依赖于这段代码的应用程序,你可以通过添加一个属性函数非常简单地改变它:
from decimal import Decimal ######################################################################## class Fees(object): """""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" self._fee = None #---------------------------------------------------------------------- def get_fee(self): """ Return the current fee """ return self._fee #---------------------------------------------------------------------- def set_fee(self, value): """ Set the fee """ if isinstance(value, str): self._fee = Decimal(value) elif isinstance(value, Decimal): self._fee = value fee = property(get_fee, set_fee)
现在我们可以这样做:
>>> f = Fees()
正如你所看到的,当我们以这种方式使用属性函数时,它允许fee属性设置并获取值本身而不破坏原有代码。让我们使用属性装饰器来重写这段代码,看看我们是否能得到一个允许设置的属性值。
from decimal import Decimal ######################################################################## class Fees(object): """""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" self._fee = None #---------------------------------------------------------------------- @property def fee(self): """ The fee property - the getter """ return self._fee #---------------------------------------------------------------------- @fee.setter def fee(self, value): """ The setter of the fee property """ if isinstance(value, str): self._fee = Decimal(value) elif isinstance(value, Decimal): self._fee = value #---------------------------------------------------------------------- if __name__ == "__main__": f = Fees() 上面的代码演示了如何为fee属性创建一个setter方法。你可以用一个名为@fee.setter的装饰器装饰第二个方法名也为fee的方法来实现这个。当你如下所做时,setter被调用: >>> f = Fees() >>> f.fee = "1"
Property 性质的属性具有getter、setter和deleter 方法,它们可以用做装饰器来创建该 属性的拷贝,使得被修饰的函数为访问函数。最好的解释就是使用一个例子: class C(object): def __init__(self): self._x = None @property def x(self): """I'm the 'x' property.""" return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x 这段代码与第一个例子完全相等。请确保给额外的函数与原始的属性相同的名字(此例中为x。) 返回的属性同样有构造函数参数中的fget、fset和fdel。
5. 绑定到对象的方法的特殊之处
类中定义的函数(没有被任何装饰器装饰的)是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数
OldboyStudent.learn(s1) #李坦克 is learning ,learn函数的参数是self,也就是对象自己 OldboyStudent.learn(s2) #王大炮 is learning OldboyStudent.learn(s3) #牛榴弹 is learning
类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法
强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将‘谁’本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)
s1.learn() #等同于OldboyStudent.learn(s1) s2.learn() #等同于OldboyStudent.learn(s2) s3.learn() #等同于OldboyStudent.learn(s3)
注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。
6. 对象之间交互
class Garen: #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄; camp='Demacia' #所有玩家的英雄(盖伦)的阵营都是Demacia; def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...; self.nickname=nickname #为自己的盖伦起个别名; self.aggressivity=aggressivity #英雄都有自己的攻击力; self.life_value=life_value #英雄都有自己的生命值; def attack(self,enemy): #普通攻击技能,enemy是敌人; enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。 我们可以仿照garen类再创建一个Riven类 class Riven: camp='Noxus' #所有玩家的英雄(锐雯)的阵营都是Noxus; def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻击力54; self.nickname=nickname #为自己的锐雯起个别名; self.aggressivity=aggressivity #英雄都有自己的攻击力; self.life_value=life_value #英雄都有自己的生命值; def attack(self,enemy): #普通攻击技能,enemy是敌人; enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。
实例出俩英雄
>>> g1=Garen('草丛伦') >>> r1=Riven('锐雯雯')
交互:锐雯雯攻击草丛伦,反之一样
>>> g1.life_value #盖伦初始生命 455 >>> r1.attack(g1) #瑞文攻击盖伦一下 >>> g1.life_value #盖伦声明值 401
补充:
garen_hero.Q()称为向garen_hero这个对象发送了一条消息,让他去执行Q这个功能,类似的有:
garen_hero.W()
garen_hero.E()
garen_hero.R()
注意: 对象用“点”方法调用的属性会在内里面或者它的基类中寻找,。如果不是用这种方法调用,那就是平常的变量
county="中国" class Student(object): county="中国-----" # print(county) #这里验证是用的类里面的country def __init__(self): print(county) pass def eat(self): print("eat 方法") s1=Student() #创建对象的时候调用的__init__,country是类外边的 print s1.county #country是类里面的 结果: 中国 中国-----
7. 调用绑定方法和调用未绑定方法
方法仅仅是类内部定义的函数(意味着方法是类属性而不是实例属性)
调用绑定方法: 创建类—>创建类的实例—>用实例调用方法
调用未绑定的方法:应用的主要场景是 你在派生一个子类,而且要覆盖覆盖父类的方法。 如:子类覆盖父类的构造器__init__()。带有实例参数的未绑定方法调用是 可以的: P.__init__(self,name,age)
class A(object): def __init__(self,a,b): self.a = a self.b = b def test(self,other=None): print ("a is %d,b is %d,other is %s" %(self.a,self.b,other)) self.a_b = self.a + self.b return self.a_b class B(A): #B继承A def __init__(self,a,b,c): 定义了自己的构造器,基类构造器不会自动调用。 A.__init__(self,a,b) 显示调用基类的构造器。参数必须与基类参数一致, 这里相当于代替了self.a = a 和self.b=b self.c = c def t(self): print ("a is %d,b is %d,c is %d" %(self.a,self.b,self.c)) if __name__ == "__main__": b = B(3,4,5) b.test() b.t()
8. 其他
方法可以直接访问实例的数据(self.data):即在一个方法中可以访问类中的所有实例属性(self.data), 也可访问其它方法中的数据(self.data)。
比如: 方法1执行后产生了一个selef.workbook数据,那么在方法2中就可以去调用它。但是要注意 必须先调用方法1再调用方法2.其它方法中的实例数据(self.data)相当于是在调用方法
后绑定给实例的,所以 要想在另外的方法中调用该属性,就的先调用该方法生成实例属性,不然会报错:AttributeError: ‘A’ object has no attribute ‘a_b’ 例子如下: class A(object): def __init__(self,a,b): self.a = a self.b = b def test(self,other=None): print (“a is %d,b is %d,other is %s” %(self.a,self.b,other)) self.a_b = self.a + self.b return self.a_b def tt(self,c): #类的方法除了多了一个self参数,其余和一般函数一样,可以有自己的参数。 c = self.a_b+c print (“c is %d” %c) if __name__ == “__main__”: t = A(1,2) print t.a,t.b print t.test(other=3) 得先调用test方法生成self.a_b,tt方法中才能调用self.a_b。 t.tt(4)