day20-面向对象基础
面向对象基础
面向过程编程与面向对象编程
面向过程编程
面向过程编程的核心是过程,过程指的是解决问题的步骤。基于面向过程编程的思想编写程序好比在设计一条流水线,是一种机械式的思维方式。
- 优点:逻辑清晰(逻辑一步一步的,比较系统)
- 缺点:扩展性差(上一个函数的输出是下一个函数的输入,如果改动某项功能时会影响其他功能)
面向对象编程
面向对象编程的核心是对象,在python中一切皆对象,对象就是特征和技能的结合体。基于面向对象编程就是定义出一个个鲜明独特的对象,然后通过对象之间交互编程
- 优点:扩展性非常强
- 缺点:编程的复杂度要高于面向过程
类与对象
类
类的意思是分类、类别。比如在学校中:按照学生和老师可以划分两类,一个是学生类,他们都具有名字、学号、班级以及选课、听课;一个是老师类,他们都有名字以及授课。因此给出类的定义:类就是一系列对象相似的特征与技能的结合体
对象
在python中一切皆对象,根据划分称为某类下的一个对象,比如猫是动物类的对象,人是人类的对象
在显示世界中:先有对象,再有类;在程序中,务必保证先定义类,后产生对象
定义类和对象
定义类:
# 注意类中定义变量使用驼峰体
class OldboyStudent:
school = 'oldboy'
def choose_course(self):
print('is choosing course')
oldboystudent_dict = OldboyStudent.__dict__ ## 双下划线开头的方法会在某种情况下自动触发
曾经定义函数,函数只检测语法,不执行代码,但是定义类的时候,代码会在类定义阶段就立刻执行,并且会产生一个类的名称空间,也就是说类的本身其实就是一个容器/名称空间,是用来存放名字的,这是类的用途之一
print(oldboystudent_dict) # 打印的是该类名称空间里具有的名字,如我们定义的school,choose_course和内置的其他属性
{'__module__': '__main__', 'school': 'oldboy', 'choose_course': <function OldboyStudent.choose_course at 0x000001BCAD3A2048>, '__dict__': <attribute '__dict__' of 'OldboyStudent' objects>, '__weakref__': <attribute '__weakref__' of 'OldboyStudent' objects>, '__doc__': None}
print(oldboystudent_dict['school']) # 取school的值
oldboy
print(oldboystudent_dict['choose_course'])
print(type(oldboystudent_dict['choose_course']))
<function OldboyStudent.choose_course at 0x000001BCAD3A2048>
<class 'function'>
oldboystudent_dict['choose_course']('nick')
is choosing course
print(OldboyStudent.school)
oldboy
OldboyStudent.choose_course('nick')
is choosing course
定义对象:调用类即可产生对象,调用类的过程,又称为类的实例化,实例化的结果称为类的对象/实例
stu1 = OldboyStudent() # 调用类会得到一个返回值,该返回值就是类的一个具体存在的对象/实例
print(1, stu1.school)
stu1.choose_course()
stu2 = OldboyStudent() # 调用类会得到一个返回值,该返回值就是类的一个具体存在的对象/实例
print(2,stu2.school)
stu2.choose_course()
1 oldboy
is choosing course
2 oldboy
is choosing course
定制对象独有特征
类中定义的函数是类的函数属性,类可以使用,对象也可以使用。如果类的属性改了,则对象的属性也会随之改变。但同一类中的对象都会具自己独特的特征,比如张三身高180,李四身高175。
class OldboyStudent:
school = 'oldboy'
def choose_course(self):
print('is choosing course')
stud1 = OldboyStudent()
stud2 = OldboyStudent()
print(stud1.__dict__)
{}
print(stud2.__dict__)
{}
对象本质类似于类,也是一个名称空间,但是对象的名称空间存放对象独有的名字,而类中存放的是对象们共有的名字。因此我们可以直接为对象单独定制名字。
stud1.name = 'nick'
stud1.age = 18
print(stud1.name,stud1.age)
nick 18
print(OldboyStudent.name) # 在对象中定义的独有属性,不属于类
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-49-d5096aff41d0> in <module>
----> 1 print(OldboyStudent.name)
AttributeError: type object 'OldboyStudent' has no attribute 'name'
使用上述方法虽然让我们定制属性更简单,但是还是太麻烦了,如果可以在实例化对象的时候自动触发定时属性,那就更方便了,因此可以使用类的__init__方法。
class OldboyStudent:
school = 'oldboy'
# 调用类的时候自动触发
def __init__(self, name, age):
self.name = name
self.age = age
def choose_course(self):
print('is choosing course')
stud1 = OldboyStudent('nick', 18)
print(stud1.__dict__, stud1.name, stud1.age)
{'name': 'nick', 'age': 18} nick 18
通过上述现象可以发现,调用类的时候创造了一个空对象,同时自动触发类中__init__功能,将stud1以及调用类括号内的参数一同传入
对象属性查找顺序
class OldboyStudent:
school = 'oldboy'
name = 'tank'
# 调用类的时候自动触发
def __init__(self, name, age):
self.name = name
self.age = age
def choose_course(self):
print('is choosing course')
stud1 = OldboyStudent('nick', 18)
print(stud1.name, stud1.school)
nick oldboy
print(stud1.height)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-61-ff2d39934944> in <module>
----> 1 print(stud1.height)
AttributeError: 'OldboyStudent' object has no attribute 'height'
- 类中定义name为tank,定义对象时传入name为nick,在打印stud1.name时打印的是nick。可见:当类和对象同时定义属性时,先从对象本身查找
- 类中定义school为oldboy,对象中未定义,打印stud1.school时打印的是oldboy。可见:当对象中未定义该属性时,从类中查找
- 类和对象都未定义时,则报错
总结:查找属性先从对象本身查找,对象没有去类中查找,类中也没有则报错
类与对象的绑定方法
class OldboyStudent:
school = 'oldboy'
name = 'tank'
# 调用类的时候自动触发
def __init__(self, name, age):
self.name = name
self.age = age
def choose_course(self):
print('is choosing course')
stud1 = OldboyStudent('nick', 18) # 每次实例化对象的时候都会自动调用__init__方法(排在第一位)
stud1.choose_course()
is choosing course
OldboyStudent.choose_course() # 报错,提示缺少参数self
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-77-d104c34c78e5> in <module>
----> 1 OldboyStudent.choose_course() # 报错,提示缺少参数self
TypeError: choose_course() missing 1 required positional argument: 'self'
OldboyStudent.choose_course('nick') # 将nick传给形参self,类似于普通函数调用
is choosing course
在类调用类中定义的函数时,需要传值给self;在对象调用类中定义的函数时,不需要传入参数。
总结:
- 类中定义的函数是类的函数属性,类可以使用,但使用的就是一个普通的函数而已,意味着需要完全遵循函数的参数规则,该传几个值就传几个
- 类中定义的函数对象也可以使用,而且是绑定给对象用的,绑定给谁,就应该由谁来调用,谁来调用就会将谁当作第一个参数自动传入。所以不需要传参给self
- 类定义的函数大多情况下都是绑定给对象用的,所以在类中定义的函数都应该自带一个参数self
类与数据类型
之前在学习定义数据时,比如定义一个列表,可以使用lis = list([1,2,3])
。其实list就是一个类,lis就是list类中的一个实例化对象。只不过这个list类是python为我们封装好的,我们可以直接调用生成一个列表。而append
就是list类定义的一个函数方法,对象lis也可以直接调用。其他数据类型也是同理
总结:python3中统一了类与类型的概念,类就是类型。因此数据类型就是将数据分成不同的类,生成一个具体的数据时就是在生成一个类的对象
对象的高度整合
- 以未来我们要连接数据库举例,如果没有面向对象的思想,我们只要想要使用一个方法,就必须得这样做
import pymysql # 连接mysql的三方库,可以pip3 install pymysql安装
def exc1(host, port, db, charset, sql):
conn = pymysql.connect(host, port, db, charset)
conn.execute(sql)
return xxx
def exc2(proc_name):
conn = pymysql.connect(host, port, db, charsett)
conn.call_proc(sql)
return xxx
exc1('1.1.1.1', 3306, 'db1', 'utf-8', 'select * from t1')
exc1('1.1.1.1', 3306, 'db1', 'utf-8', 'select * from t2')
exc1('1.1.1.1', 3306, 'db1', 'utf-8', 'select * from t3')
exc1('1.1.1.1', 3306, 'db1', 'utf-8', 'select * from t4')
- 有了面向对象之后,对于上述的例子,我们可以这样做
import pymysql
class Foo:
def __init__(self, host, port, db, chartset):
self.host = host
self.port = port
self.db = db
self.charset = chartset
def exc1(self, sql):
conn = pymysql.connect(self.host, self.port, self.db, self.charset)
conn.execute(sql)
return xxx
def exc2(self, proc_name):
conn = pymysql.connect(self.host, self.port, self.db, self.charsett)
conn.call_proc(sql)
return xxx
obj1 = Foo('1.1.1.1', 3306, 'db1', 'utf-8')
obj1.exc1('select * from t1')
obj1.exc1('select * from t2')
obj1.exc1('select * from t3')
obj1.exc1('select * from t4')
总结:对象其实就是一个高度整合的产物,整合数据与专门操作该数据的方法(绑定方法)