面向对象编程
- 什么是面向对象?
面向过程:将需要解决的问题按步骤划分,一步一步完成每一个步骤,而且 步骤之间有联系。
优点:复杂问题可以分步完成
缺点:扩展性很差,维护性差。如果中间某一环节有问题整体都会发生问题
使用的场景:对扩展性要求较低的软件,比如系统软件,脚本程序
面向对象(oop):将程序看作一堆对象的集合,实现功能的方式就是通过对象之间的交互来实现。
优点:扩展性高。对象之间的影响几乎没有
缺点:当某些程序不需要扩展性时书写就很复杂,无法预知结果 - 什么是对象?
对象就是具备某些特征与技能的结合体 - 什么是类?
类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体 - 类与对象之间的关系
类的作用是标识对象与对象之间的差异
对象是某个类的实例。对象是实际的,类是抽象的 - 先有类还是先有对象?
在现实世界中:先有对象,再总结出类
在编程中:先有类,才能创建符合类特征的对象
总结:面对对象的思想就是用类和对象来解决问题
面对对象编程的本质:通过使用不同的对象来完成程序
1.类和对象的使用
- 类
类的定义:
怎么定义类?
class ThisIsPerson: #定义一个ThisIsPerson的对象
#用变量描述特性
name = 'ming'
age = 28
#这就是一个类的定义
#注意类名在书写上用大驼峰体,每个英文字母首字母大写
2.对象
对象怎么来?
class ThisIsPerson:
name = 'ming'
age = 28
#得到对象
obj = ThisIsPerson()
#调用类可以获得对象,调用也称为实例化或创建对象
print(obj)
<__main__.ThisIsPerson object at 0x000001CCA7C38390>
#使用对象的属性
print(obj.name)
print(obj.age)
ming
28
#注意:
1.类中可以有任意python代码,这些代码在类定义阶段便会执行
2.因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过ThisIsPerson.__dict__查看
3.对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法
4.点是访问属性的语法,类中定义的名字,都是类的属性
#
#例如
#创建了两个对象
obj1 = ThisIsPerson()
obj2 = ThisIsPerson()
print(obj1)
print(obj2)
<__main__.ThisIsPerson object at 0x000001E381B684A8>
<__main__.ThisIsPerson object at 0x000001E381B684E0>
#我们通过两个对象说明了内存地址不一样
print(obj1.name)
print(obj2.name)
ming
ming
print(id(obj1.name))
print(id(obj2.name))
2048312521536
2048312521536
#两个对象的name属性都一样,而且ID指向同一个内存地址,这是因为name的属性是在类中创建的所以每个对象使用的都是同一份属性
#为对象单独制定属性
obj1.name = 'xian'
print(obj1.name)
print(obj2.name)
xian
ming
print(obj1.__dict__)
{'name': 'xian'}
#输出的结果说明对象可以存放属性,有自己的名称空间
#注意:属性的存放位置
1.存放属性的位置有两个,一个在类中,一个在对象中
2.当每个对象的某个特征都想同时,属性应该放在类中
3.当每个对象的某个特征不同时,这个属性就应该放在对象中
4.通过.__dict__可以获取对象中包含的内容
2.类和对象中属性被访问的顺序
#我们还是拿刚才的例子说明
class ThisIsPerson:
name = 'ming'
age = 28
obj1 = ThisIsPerson()
obj2 = ThisIsPerson()
print(obj1.__dict__)
print(obj2.__dict__)
{} #我们查看对象obj1,obj2,发现其中都没有内容。
{} #为什么会有name属性?
print(obj1.name)
print(obj2.name)
ming
ming #两个对象都获得了name属性ming
#1.说明当对象中没有属性时,对象会去找类要该属性
obj1.name = 'xian' #给obj1换个name属性
print(obj1.__dict__)
print(obj2.__dict__)
{'name': 'xian'}
{}
print(obj1.name)
print(obj2.name)
xian #输出xian说明使用了对象的属性
ming
#2.说明对象中如果存在这个属性,优先使用对象中的属性
#注意
1.当创建一个类的时候会产生一个存放类属性的名称空间,存储类中属性和值的绑定关系
2.当生成一个对象时,同样也会产生一个存放对象属性的名称空间,存储对象中属性和值的绑定关系。
3.初始化函数(__init__方法)
#前面我们已经知道了类的定义以及类和对象的生成,类和对象的属性设置
class ThisIsPerson:
name = 'ming'
age = 28
#如果现在我有三个对象需要生成。按照之前办法
#生成三个对象
obj1 = ThisIsPerson()
obj2 = ThisIsPerson()
obj3 = ThisIsPerson()
#现在,这三个对象的属性都不相同,添加不同属性
obj1.name = 'yanghetiji'
obj1.age = 25
obj2.name = 'qiaoshangtiji'
obj2.age = 24
obj3.name = 'banshangpotiji'
obj3.age = 26
print(obj1.__dict__)
print(obj2.__dict__)
print(obj3.__dict__)
{'name': 'yanghetiji', 'age': 25}
{'name': 'qiaoshangtiji', 'age': 24}
{'name': 'banshangpotiji', 'age': 26}
#我们需要一个个添加不同属性,如果有一万个?怎么办?
#我们肯定想到用一个函数去处理这些数据不用每次都写重复代码
#那我们定义一个函数,用于设置属性
def set_attribute(obj, name, age):
obj.name = name
obj.age = age
obj1 = ThisIsPerson()
obj2 = ThisIsPerson()
obj3 = ThisIsPerson()
set_attribute(obj1,'yanghetiji',25) #调用函数,添加属性
set_attribute(obj2,'qiaoshangtiji',24)
set_attribute(obj3,'banshangpotiji',26)
print(obj1.__dict__)
print(obj2.__dict__)
print(obj3.__dict__)
#输出
{'name': 'yanghetiji', 'age': 25}
{'name': 'qiaoshangtiji', 'age': 24}
{'name': 'banshangpotiji', 'age': 26}
#这种方式每次添加还是需要调用一次函数,我们设想下可不可以将这个函数写到类中,让后去调用
class ThisIsPerson:
def set_attribute(obj, name, age):
obj.name = name
obj.age = age
obj4 = ThisIsPerson()
ThisIsPerson.set_attribute(obj4, 'liuhetiji', 23)
print(obj4.__dict__)
{'name': 'liuhetiji', 'age': 23}
#可以写入到类中,但是还是很麻烦,再次优化
#我们知道,这些属性是我们对象在产生时就应该有的,因为光说'tieji'你不知道☞那个。这个时候我们需要用我们的内置方法__init__方法了
class ThisIsPerson:
def __init__(self, name, age):
#第一个参数表示初始化的对象本身,这其实也是一个对象方法
self.name = name
self.age = age
obj5 = ThisIsPerson('daidai', 26)
print(obj5.__dict__)
{'name': 'daidai', 'age': 26}
# 强调:
# 1、该方法内可以有任意的python代码
# 2、一定不能有返回值
# 3、该方法参数self建议不要修改,而且解释器会自动传值,不要传参数
4.类与对象的绑定方法
- 什么是绑定方法?
方法就是函数,面向对象编程中函数就是方法。绑定方法就是对象或类与函数进行绑定。
绑定方法有两种,一种是绑定给对象,一种是绑定给类
- 为什么要绑定?
对象本质上就是存放数据的容器,绑定方法就是将函数与数据绑定在一起,这个函数的功能就是处理数据。
- 为什么要将这样的函数和数据绑定?如何使用?
#1.对象的绑定方法
class ThisIsPerson:
address = 'LiuHe'
def __init__(self, name, age):
self.name = name
self.age = age
def print_info(self): #这就是一个绑定给对象的方法
print('%s😄%s是老板,%s岁' % (self.address, self.name, self.age))
obj6 = ThisIsPerson('tiandi', 23)
#注意,用对象来调用没有传入参数,因为参数就是调用的对象本身
obj6.print_info()
LiuHe😄tiandi是老板,23岁
#1.默认情况下,在类中定义的函数就是绑定方法
#2.当用对象本身来调用类中的方法时,默认会把对象本身作为参数传入方法中。而用类名调用时则需要传入参数(对象)。
obj6 = ThisIsPerson('tiandi', 23)
ThisIsPerson.print_info(obj6)
#这就是用类名进行调用,必须传入被绑定对象作为参数
LiuHe😄tiandi是老板,23岁
总结:
#1.默认情况下,在类中定义的函数就是绑定方法
#2.当用对象本身来调用类中的方法时,默认会把对象本身作为参数传入方法中。而用类名调用时则需要传入参数(对象)。
#2.类的绑定方法
class ThisIsPerson:
address = 'LiuHe'
def __init__(self, name, age):
self.name = name
self.age = age
def print_info(self):
print('%s😄%s是老板,%s岁' % (self.address, self.name, self.age))
@classmethod
def print_address(cls): #这就是一个类的绑定方法
print('hello,here is%s' %ThisIsPerson.address)
print(cls)
#用类来调用时候
ThisIsPerson.print_address()
#调用结果
#hello,here isLiuHe
#<class '__main__.ThisIsPerson'>
#用对象来调用的时候
obj7 = ThisIsPerson('mingsheng', 26)
obj7.print_address()
#结果不变
#注意!
1.绑定给类的方法是利用解释器内置的装饰器 @classmethod 定义类的绑定方法时参数cls就是被绑定类本身,建议不要修改
2.类的绑定方法在调用时候可以用类或者对象调用,都不需要传入参数
#分析:一个方法到底应该绑定给对象还是绑定给类呢?
1.当需要处理的数据包含在类中,就应该绑定给类
2.当需要处理的数据包含在对象中,就应绑定给对象
#3.非绑定方法
#既不绑定给类,也不绑定给对象
#使用在数据既不需要访问类,也不需要访问对象
class ThisIsPerson:
address = 'LiuHe'
def __init__(self, name, age):
self.name = name
self.age = age
def print_info(self):
print('%s😄%s是老板,%s岁' % (self.address, self.name, self.age))
@classmethod
def print_address(cls):
print('hello,here is%s' %ThisIsPerson.address)
print(cls)
@staticmethod
def test_func(*args, **kwargs): #非绑定方法
print('这是一个非绑定方法')
#通过类调用非绑定方法
ThisIsPerson.test_func()
#通过对象调用非绑定方法
obj7 = ThisIsPerson('mingsheng', 26)
obj7.test_func(100)
#这是一个非绑定方法
#这是一个非绑定方法
代码层面理解类与对象
#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('存储过程的名字')
数据与专门操作该数据的功能组合到一起