一、绑定方法与非绑定方法
1.绑定方法
绑定给谁,谁来调用就自动将它本身当作第一个参数传入
(1)绑定到类的方法:用classmethod装饰器装饰的方法。
为类量身定制
类.boud_method(),自动将类当作第一个参数传入
(其实对象也可调用,但仍将类当作第一个参数传入)
(2)绑定到对象的方法:没有被任何装饰器装饰的方法。
为对象量身定制
对象.boud_method(),自动将对象当作第一个参数传入
(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
2.非绑定方法
用staticmethod装饰器装饰的方法
不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已
注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说。
3.staticmethod用法
statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果,python为我们内置了函数staticmethod来把类中的函数定义成静态方法。
import hashlib
import time
class MySQL:
def __init__(self,host,port):
self.id=self.create_id()
self.host=host
self.port=port
@staticmethod
def create_id(): #就是一个普通工具
m=hashlib.md5(str(time.clock()).encode('utf-8'))
return m.hexdigest()
print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数
conn=MySQL('127.0.0.1',3306)
print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数
4.classmethod
classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数(即便是对象来调用也会将类当作第一个参数传入),python为我们内置了函数classmethod来把类中的函数定义成类方法。
#建立settings.py文件,内容如下
HOST='127.0.0.1'
PORT=3306
DB_PATH=r'C:UsersAdministratorPycharmProjects est面向对象编程 est1db'
import settings
import hashlib
import time
class MySQL:
def __init__(self,host,port):
self.host=host
self.port=port
@classmethod
def from_conf(cls):
print(cls)
return cls(settings.HOST,settings.PORT)
print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
conn=MySQL.from_conf()
print(conn.host,conn.port)
conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类
5.用法示例
class Foo:
@classmethod
def test(cls):
print(cls)
print(Foo.test)
f=Foo()
Foo.test()
print(Foo)
f.test()
运行结果:
<bound method Foo.test of <class '__main__.Foo'>>
<class '__main__.Foo'>
<class '__main__.Foo'>
<class '__main__.Foo'>
class Foo:
@staticmethod
def test(x,y):
print('test',x,y)
Foo.test(1,3)
f=Foo()
f.test(1,10)
运行结果:
test 1 3
test 1 10
class Foo:
def test1(self):
pass
@classmethod
def test2(cls):
print(cls)
@staticmethod
def test3():
pass
f=Foo()
print(f.test1)
print(Foo.test2)
print(Foo.test3)
print(f.test3)
运行结果:
<bound method Foo.test1 of <__main__.Foo object at 0x0000000001071C50>>
<bound method Foo.test2 of <class '__main__.Foo'>>
<function Foo.test3 at 0x0000000001073598>
<function Foo.test3 at 0x0000000001073598>
import settings
class MySQL:
def __init__(self,host,port):
self.host=host
self.port=port
print('conneting...')
@classmethod
def from_conf(cls):
return cls(settings.HOST,settings.PORT) #MySQL('127.0.0.1',3306)
def select(self): #绑定到对象的方法
print(self)
print('select function')
conn=MySQL('192.168.1.3',3306)
conn.select()
conn1=MySQL('192.168.1.3',3306)
conn2=MySQL.from_conf()
运行结果:
conneting...
<__main__.MySQL object at 0x00000000006D1DA0>
select function
conneting...
conneting...
ID随机数
import hashlib
import time
def create_id():
m=hashlib.md5(str(time.clock()).encode('utf-8'))
return m.hexdigest()
print(time.clock())
print(time.clock())
print(time.clock())
print(create_id())
print(create_id())
print(create_id())
print(create_id())
运行结果:
3.109938180648845e-07
4.074019016649987e-05
5.038099852651129e-05
5d14f5b3c90498d0b8237983016d86b8
6e4933ec7bb88b8a2b3ec28459687d96
70c803728dc6aa2cbed7e516a434beb8
7a69d8e021a2cb842a53f6bf50de82c6
ID随机数应用
import hashlib
import time
import settings
class MySQL:
def __init__(self,host,port):
self.id=self.create_id()
self.host=host
self.port=port
print('conneting...')
@staticmethod
def create_id(): #非绑定方法,就是类中的普通工具包
m=hashlib.md5(str(time.clock()).encode('utf-8'))
return m.hexdigest()
@classmethod
def from_conf(cls):
return cls(settings.HOST,settings.PORT) #MySQL('127.0.0.1',3306)
def select(self): #绑定到对象的方法
print(self)
print('select function')
conn=MySQL('192.168.1.3',3306)
conn.select()
conn1=MySQL('192.168.1.3',3306)
conn1=MySQL.from_conf()
conn2=MySQL.from_conf()
conn3=MySQL.from_conf()
conn4=MySQL.from_conf()
print(conn1.id)
print(conn2.id)
print(conn3.id)
print(conn4.id)
运行结果:
conneting...
<__main__.MySQL object at 0x0000000000A6F400>
select function
conneting...
conneting...
conneting...
conneting...
conneting...
b70978d582704ddef008747f6a712abe
dcab93c2bcdb90f99149eb80ac002aaa
eb7a07a25ce10501e795454615809725
88491deb201c4a704a9e9376eb0dc787
statcimethod 与classmethod的区别
import settings
class MySQL:
def __init__(self,host,port):
self.host=host
self.port=port
# print('conneting...')
@classmethod
def from_conf(cls):
return cls(settings.HOST,settings.PORT) #Mariadb('127.0.0.1',3306)
@staticmethod
def from_conf():
return MySQL(settings.HOST, settings.PORT) # MySQL('127.0.0.1',3306)
def __str__(self):
return '就不告诉你'
conn=MySQL.from_conf()
print(conn.host)
class Mariab(MySQL):
def __str__(self):
return 'host:%s port:%s' %(self.host,self.port)
pass
conn1=Mariab.from_conf()
print(conn1)
运行结果:
127.0.0.1
就不告诉你
import settings
class MySQL:
def __init__(self,host,port):
self.host=host
self.port=port
@staticmethod
def from_conf():
return MySQL(settings.HOST,settings.PORT)
# @classmethod
# def from_conf(cls):
# return cls(settings.HOST,settings.PORT)
def __str__(self):
return '就不告诉你'
class Mariadb(MySQL):
def __str__(self):
return '主机:%s 端口:%s' %(self.host,self.port)
m=Mariadb.from_conf()
print(m) #我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行,打印就不告诉你:
定义MySQL类
1)对象有id、host、port三个属性
2)定义工具create_id,在实例化时为每个对象随机生成id,保证id唯一
3)提供两种实例化方式,方式一:用户传入host和port 方式二:从配置文件中读取host和port进行实例化
4)为对象定制方法,save和get,save能自动将对象序列化到文件中,文件名为id号,文件路径为配置文件中DB_PATH;get方法用来从文件中反序列化出对象
import settings
import hashlib
import time
import random
import pickle
import os
class MySQL:
def __init__(self,host,port):
self.id=self.create_id()
self.host=host
self.port=port
def save(self):
file_path=r'%s%s%s' %(settings.DB_PATH,os.sep,self.id)
pickle.dump(self,open(file_path,'wb'))
def get(self):
file_path = r'%s%s%s' % (settings.DB_PATH, os.sep, self.id)
return pickle.load(open(file_path,'rb'))
@staticmethod
def create_id():
m=hashlib.md5(str(time.clock()).encode('utf-8')) #查看clock源码注释,指的是cpu真实时间,不要用time.time(),否则会出现id重复
return m.hexdigest()
@classmethod
def from_conf(cls):
print(cls)
return cls(settings.HOST,settings.PORT)
print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
conn=MySQL.from_conf()
print(conn.id)
print(conn.create_id())
print(MySQL.create_id())
print(conn.id)
conn.save()
obj=conn.get()
print(obj.id)
其他练习
import time
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)
运行结果:
1987 11 27
2017 6 13
2017 6 14
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 0x0000000001091D68>
因为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:6 day:13
二、面向对象的软件开发
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提出的思路,用面向对象语言编写出程序既可。
在一个大型软件开发过程中,OOP只是很小的一个部分。
对于全栈开发的你来说,这五个阶段都有了,对于简单的问题,不必严格按照这个5个阶段进行,往往由程序设计者按照面向对象的方法进行程序设计,包括类的设计和程序的设计。
三、python中关于OOP的常用术语
1.抽象/实现
抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。
对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。
2.封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。
注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”
真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明
(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
3.合成
合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。
4.派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。
5.泛化/特化
基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。
6.多态与多态性
多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气
多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。
冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样
7.自省/反射
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__
四、问题总结
1.什么样的代码才是面向对象?
从简单来说,如果程序中的所有功能都是用类和对象来实现,那么就是面向对象编程了。
2.函数式编程 和 面向对象 如何选择?分别在什么情况下使用?
须知:对于 C# 和 Java 程序员来说不存在这个问题,因为该两门语言只支持面向对象编程(不支持函数式编程)。而对于 Python 和 PHP 等语言却同时支持两种编程方式,且函数式编程能完成的操作,面向对象都可以实现;而面向对象的能完成的操作,函数式编程不行(函数式编程无法实现面向对象的封装功能)。
所以,一般在Python开发中,全部使用面向对象 或 面向对象和函数式混合使用
面向对象的应用场景:
(1)多函数需使用共同的值,如:数据库的增、删、改、查操作都需要连接数据库字符串、主机名、用户名和密码
class SqlHelper:
def __init__(self, host, user, pwd):
self.host = host
self.user = user
self.pwd = pwd
def 增(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接
def 删(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接
def 改(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接
def 查(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接# do something
(2)需要创建多个事物,每个事物属性个数相同,但是值的需求
如:张三、李四、杨五,他们都有姓名、年龄、血型,但其都是不相同。即:属性个数相同,但值不相同
class Person:
def __init__(self, name ,age ,blood_type):
self.name = name
self.age = age
self.blood_type = blood_type
def detail(self):
temp = "i am %s, age %s , blood type %s " % (self.name, self.age, self.blood_type)
print temp
zhangsan = Person('张三', 18, 'A')
lisi = Person('李四', 73, 'AB')
yangwu = Person('杨五', 84, 'A')
3.类和对象在内存中是如何保存?
类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,大致如下图:
如上图所示,根据类创建对象时,对象中除了封装name和age的值之外,还会保存一个类对象指针,该值指向当前对象的类。
当通过obj1执行 【方法一】 时,过程如下:
- 根据当前对象中的类对象指针找到类中的方法
- 将对象obj1当作参数传给方法的第一个参数self