面向过程
面向过程和面向对象是编程的两大编程思想,而不是一门新的技术栈
什么是面向过程?
面向过程核心是过程,过程就是先干什么再干什么,最后在干什么,就是机械式的思维方式
例子:
把大象装进冰箱需要几步
1.把冰箱门打开
2.把大象装进冰箱
3.关上冰箱门
面向过程的优缺点
优点:将复杂的事情的简单化,流程化
缺点:扩展性差,牵一发而动全身
应用场景
一般用在对扩展性要求比较差的情况
下面我们将使用面向过程写一个注册的案例来体会面向过程的优缺点
1. 先让用户输入用户名和密码
2. 验证参数
3. 直接把数据写入文件中
# 1,让用户输入用户名和密码
def get_userinfo():
username=input('username>>>>>').strip()
password=input('password>>>>>').strip()
return {
'username':username,
'password':password
}
# 2,验证参数
def check_info(userinfo):
flag=True
if len(userinfo['username'])==0:
print('用户名不能为空')
flag=False
if len(userinfo['password'])==0:
print('密码不能为空')
flag=False
return{
'flag':flag,
'userinfo':userinfo
}
# 3.直接把数据写入文件中
def save_info(param):
#用来保存数据到文件中
''' param={
'flag':flag,
'userinfo':userinfo
}
:param param:
:return:
'''
if param['flag']:
with open('userinfo.txt','w',encoding='utf-8')as f:
import json
json.dump(param['userinfo'],f)
def main():
userinfo=get_userinfo()
param=check_info(userinfo)
save_info(param)
if __name__=='__main__':
main()
面向对象编程
对象的概念
面向对象的核心是'对象'二字,而对象的精髓在于'整合'
所有的程序都是由'功能'和'数据'组成,因而编写程序的本质就是定义出一系列的功能来对数据进行操作,在学习'之前',程序中的数据与功能是分离开的
比如
# 数据:name、age、sex
name='lili'
age=18
sex='female'
# 功能:tell_info
def tell_info(name,age,sex):
print('<%s:%s:%s>' %(name,age,sex))
# 此时若想执行查看个人信息的功能,需要同时拿来两样东西,一类是功能tell_info,另外一类则是多个数据name、age、sex,然后才能执行,非常麻烦
tell_info(name,age,sex)
在学习了对象之后我们就有了一个容器,该容器可以盛放数据与功能,所以我们可以说:对象就是把数据与功能整合到一起的产物,或者说'对象'就是一个盛放数据与功能的容器/盒子/箱子
面向对象的优缺点
优点:扩展性强
缺点:简单的问题复杂化了
类的定义和对象的产生
类即类别/种类,是面向对象分析和设计的基石,如果多个对象有相似的数据和功能,那么该多个对象就属于同一种类,有了类的好处是:我们可以把同一类对象相同的数据和功能存放到类里,而无需每个对象都重复存一份,这样每个对象里只需要存自己独有的数据即可,极大的节省了空间,所所以,如果说对象是用来存放数据与功能的容器,那么类则是用来存放多个对象相同的数据与功能的容器。
类与对象关系图
综上所述,虽然我们是先介绍对象后介绍类,但是需要强调的是:在程序中,必须要事先定义类,然后再调用类产生对象(调用类拿到的返回值就是对象)。产生对象的类与对象之间存在关联,这种关联指的是:对象可以访问到类中共有的数据与功能,所以类中的内容仍然是属于对象的,类只不过是一种节省空间、减少代码冗余的机制,面向对象编程最终的核心仍然是去使用对象。
在程序中:一定是先有类,然后调用类,产生对象!!!
类的定义
"""
专业语法:
类的定义
class Student():
pass
"""
1. 关键字使用class来声明
2. class后面写的是类名,类名的命名:一般情况下遵循变量的命名规范即可,类名一般首字母都要大写,(小写也行,但是不推荐)
3. 如果你的类名比较长,推荐使用驼峰法,并且是大驼峰
4. 类名后面的小括号不能省略
下面让我们用选课系统来定义一个类
# 学生1:
数据:
学校=清华大学
姓名=李建刚
性别=男
年龄=28
功能:
选课
# 学生2:
数据:
学校=清华大学
姓名=王大力
性别=女
年龄=18
功能:
选课
# 学生3:
数据:
学校=清华大学
姓名=牛嗷嗷
性别=男
年龄=38
功能:
选课
我们可以总结出一个学生类,用来存放学生们相同的数据与功能
# 学生类
相同的特征:
学校=清华大学
相同的功能:
选课
基于上述分析的结果,我们接下来需要做的就是在程序中定义出类,然后调用类产生对象
class Student():
school = '清华大学' # 类属性,其实就是变量吗?
def choose_course(stu_dict, course):
stu_dict['courses'].append(course)
print("%s选课成功:%s" % (stu_dict['name'], stu_dict['courses']))
print(123)
定义类发生了哪些事情?
1. 类一旦定义完成,会立马执行类体代码
2. 会产生类的名称空间,其实就是一个大字典,然后把类中得名字都丢到类的名称空间去
3. 把类的名称空间绑定给 类名.__dict__中
如何查看类的名称空间?
print(Student.__dict__)
如何产生对象?
# 调用类产生对象
stu = Student()
print(stu)
如何查看对象的名称空间
print(stu.__dict__) # {} 默认情况下调用类会得到一个空对象,其实就是一个空字典
调用类的过程也是实例化的过程,得到的对象也称为是一个实例
定制对象自己独有的属性
默认情况下,调用类产生一个对象,它是一个空对象,没有任何的数据
因此,我们需要给每一个对象添加数据
### 版本3:
class Student():
school = 'SH' # 类属性,其实就是变量吗?
"""它有特殊的含义, 在类里面必须叫__init__,然后调用类的时候才会自动触发执行"""
## 类里面一定要有这个方法吗? 不一定,也就是说,你如果需要提前定制对象的属性,你就写这个方法, 如果不需要,就不用写
def __init__(self, name, age, gender, course=None):
if course is None:
course = []
self.name = name # stu.__dict__['name'] = 'kevin'
self.age = age # stu.__dict__['age'] = 18
self.gender = gender # stu.__dict__['gender'] = 'male'
self.courses = course # stu.__dict__['courses'] = []
## 函数写在类里面就称为是方法
def choose_course(stu_dict, course):
stu_dict['courses'].append(course)
print("%s选课成功:%s" % (stu_dict['name'], stu_dict['courses']))
### 版本2
"""在调用类的时候,会自定调用类里面的__init__方法"""
"""调用类的时候有特殊的功能,它会把对象自己当成第一个参数传给__init__方法"""
# stu = Student('kevin', 19, 'male') # stu = Student(stu, 'kevin', 19, 'male')
# # stu1 = Student()
#
# stu1 = Student('jerry', 20, 'female')
属性的查找顺序
类属性和对象属性
在类中定义的名字,都是类的属性,细说的话,类有两种属性:数据属性和函数属性,可以通过__dict__访问属性的值,比如Student.__dict__['school'],但Python提供了专门的属性访问语法
1.显示类属性的查找
class Student():
school = 'SH'
stu = Student()
### 1. 类属性的查找
# 查
# print(Student.__dict__['school'])
print(Student.school)
# 增加
Student.x = 'xxx'
Student.z = 666
print(Student.__dict__)
# 改
Student.school = 'BJ'
print(Student.__dict__)
# 删除
del Student.school
print(Student.__dict__)
2.对象自己的属性查找
###### 对象的增删改查
stu = Student('kevin', 19, 'male')
# 1. 查
print(stu.name)
print(stu.age)
print(stu.gender)
# print(stu.x)
print(stu.school) # SH #### 如果你使用的是点语法,那么,对象查找属性的时候,先从对象自己的名称空间中查找,如果找不到,就去产生这个对象的类中查找
"""如果你使用的__dict__取值,只能够取到对象自己名称空间中得属性,不会去类中查找"""
# print(stu.__dict__['school']) # SH #### 如果你使用的是点语法,那么,对象查找属性的时候,先从对象自己的名称空间中查找,如果找不到,就去产生这个对象的类中查找
print(stu.__dict__)
## 增加
stu.x = 666
# stu1.x = 999
## 改
stu.x = 888
## 删除
# del stu.x
del stu.school
print(stu.__dict__)
绑定方法与非绑定方法
绑定方法:
特殊之处在于将调用者本身当作第一个参数自动传入
1.绑定给对象的方法:调用者是对象,自动传入的是对象
class Student():
school = 'SH'
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
"""绑定给对象来调用的,"""
def tell_info(self, username, password):
print("name:%s age:%s gender:%s %s %s" % (self.name, self.age, self.gender, username, password))
"""默认情况下,在类内部写方法是绑定给对象的,就有对象来调用,就会自动来传递参数"""
stu = Student('ly', 20, 'male')
2.绑定给类的方法:调用者是类,自动传入的是类
应用场景:就是提供一种新的造对象方式
class Oracle():
def __init__(self, ip, port):
self.ip = ip
self.port = port
"""
此时该方法就是绑定给类的,那么就有类来调用,有什么特殊之处:就是会把类自动当成第一个参数传递给方法的第一个形参cls
条件:
1. 加一个装饰器@classmethod
2. 把方法的第一个形参改为cls
3. 外部调用该方法的时候使用类来调用即可
4. 绑定给类的方法中没有self这个参数了
5. 绑定给对象的方法中,就没有cls这个参数了
"""
@classmethod
def from_func(cls):
# cls:就是类
return cls('127.0.0.1', 3306)
obj = Oracle('127.0.0.1', 3306)
非绑定方法:
没有绑定给任何人,调用者可以是类,对象,没有自动传参的次奥过
class Student():
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
self.id = self.create_id()
@staticmethod
def create_id():
"""当你在函数中不需要对象,也不需要类的时候,就把函数设置为静态方法,类能来调用,对象也能来调用,而且不用传递任何的参数"""
import uuid
return uuid.uuid4()
def func(self):
"""如果你在方法中即需要对象,也需要类,就把方法绑定给对象"""
# self.__class__ # 返回该对象的类
# self.__class__.__name__ # Student # 类名字的字符串形式的
# return self.__class__() # Student()
stu = Student('ly', 20, 'female')
print(stu.id)
print(stu.create_id())
隐藏属性
1.如何隐藏:在属性名前加__前缀,就会实现一个对外隐藏属性效果
该隐藏需要注意的问题
I:在类外部无法直接访问双下划线开头的属性,但知道了类名和属性名就可以拼出名字:_类名__属性名,然后就可以访问了,如Foo._A__N,所以说这种操作并没有严格意义上地限制外部访问,仅仅是一种语法意义上的变形
class Foo:
__x = 1 # _Foo__x
def __f1(self): # _Foo__f1
print('from test')
print(Foo.__dict__)
print(Foo._Foo__x)
print(Foo._Foo__f1)
II:这种隐藏对外不对内,因为__开头的属性会在检查类体代码语法时统一发生变形
class Foo:
__x = 1 # _Foo__x = 1
def __f1(self): # _Foo__f1
print('from test')
def f2(self):
print(self.__x) # print(self._Foo__x)
print(self.__f1) # print(self._Foo__f1)
print(Foo.__x)
print(Foo.__f1)
obj=Foo()
obj.f2()
III:这种变形操作只在检查类体语法的时候发生一次,之后定义的__开头的属性都不会变形
class Foo:
__x = 1 # _Foo__x = 1
def __f1(self): # _Foo__f1
print('from test')
def f2(self):
print(self.__x) # print(self._Foo__x)
print(self.__f1) # print(self._Foo__f1)
Foo.__y=3
print(Foo.__dict__)
print(Foo.__y)
class Foo:
__x = 1 # _Foo__x = 1
def __init__(self,name,age):
self.__name=name
self.__age=age
obj=Foo('egon',18)
print(obj.__dict__)
print(obj.name,obj.age)
2.为何要隐藏
I:隐藏数据属性 将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格的控制
# 设计者:egon
class People:
def __init__(self, name):
self.__name = name
def get_name(self):
# 通过该接口就可以间接地访问到名字属性
# print('小垃圾,不让看')
print(self.__name)
def set_name(self,val):
if type(val) is not str:
print('小垃圾,必须传字符串类型')
return
self.__name=val
# 使用者:王鹏
obj = People('egon')
# print(obj.name) # 无法直接用名字属性
# obj.set_name('EGON')
obj.set_name(123123123)
obj.get_name()
# II、隐藏函数/方法属性:目的的是为了隔离复杂度
property装饰器
它是一个内置的装饰器,它是把方法伪装成属性来使用
class Student():
__school = 'SH' # _Student__school
__country = 'CHINA'
def __init__(self, name, age, gender):
self.__name = name # _Student__name
self.age = age
self.gender = gender
def __func(self): # _Student__func
print('from func')
@property
def school(self):
return self.__school
@school.setter
def school(self, v):
print('from school')
"""隐藏属性对外不对内"""
if type(v) is str:
Student.__school = v # Student._Student__school = 'BJ'
else:
print('修改的数据必须是字符串')
@school.deleter
def school(self):
print('delete school')
del Student.__school
@property
def country(self):
return self.__school
@country.setter
def country(self, v):