类与对象
内容概要
- 面向过程编程
- 面向对象编程
- 类的定义和对象的产生
- 类属性和对象属性的查找顺序
- 绑定方法
- 非绑定方法
- 隐藏属性
- property装饰器
内容详细
一、面向过程编程
面向过程编程编程思想:
解决问题的流程,好比设计一条流水线,按顺序执行代码,先做什么,再做什么,后做什么
例子:
把大象放冰箱中需要几步?
'''
1. 把冰箱门打开
2. 把大象放进去
3. 关上冰箱门
'''
优点:把复杂的问题流程化,简单化
缺点:不易修改,牵一发而动全身,不易维护,扩展性差
二、面向对象编程
1、面向对象编程(oop)是一种程序设计思想。oop把对象作为程序的基本单元,一个对象包含数据和操作数据的函数
2、在python中,所有数据类型都被视为对象,也可以自定义对象。自定义对象数据类型就是面向对象中类的概念
面向对象核心是对象二字
什么是对象?
1. 程序中:
函数:盛放数据的容器
对象:盛放数据和函数的容器
2. 现实生活中:
一切皆对象
对象:特征与节能的结合体
eg:
优点:扩展性强,可维护性强
缺点:编程复杂度高
def choose_course(stu_dict, course):
stu_dict['course'].append(course)
print('%s选课成功 %s' % (stu_dict['name'], stu_dict['course']))
stu1 = {
'name': 'jason',
'age': 18,
'gender': 'male',
'course': [],
'choose_course': choose_course
}
stu2 = {
'name': 'ly',
'age': 18,
'gender': 'male',
'course': [],
'choose_course': choose_course
}
stu1['choose_course'](stu1, 'python')
stu2['choose_course'](stu2, 'python')
三、类的定义
对象:是属性(数据)与功能的结合体
类:一系列对象相似的特征与相似的技能的结合体
强调:站在不同的分类,划分的分类不一定一样
1. 程序中:
必须先定义类,然后调用类产生对象
2. 现实生活中:
先有对象,在有人类
'''
python的语法定义类:
1. 定义函数
def 函数名():
pass
2. 定义类:
class 类名():
pass
'''
# 定义类发生的三件事:
1. 立即执行类体代码
2. 产生一个类的名称空间,把类体里面执行的名字都扔到名称空间中(大字典)
3. 把类的名称空间绑定给__dict__, 类名.__dict__
# 约定俗成:类名一般首字母大写
class Student():
# 定义一个属性
school = 'SH'
# 定义一个技能(函数)
def choose_course(stu_dict, course):
stu_dict['course'].append(course)
print('%s选课成功 %s' % (stu_dict['name'], stu_dict['course']))
print('>>>>>>')
# 查看类的名称空间
print(Student.__dict__)
# 产生对象
# 调用类产生对象,默认产生的就是一个空对象{}
stu1 = Student()
stu2 = Student()
stu3 = Student()
print(stu1.__dict__) # {}
print(stu2.__dict__) # {}
print(stu3.__dict__) # {}
四、给对象定制自己独有的属性
class Student():
# 学生对象都有个共同的学校属性
school = 'oldboy'
# 其中也有一个魔法 __new__,会自动调用把对象赋值给一个空字典
# 初始化语法
# self 就是对象名,在实例化对象时会自动传入对象名
def __init__(self, name, age, gender, course=None)
if course = None:
course = []
self.name = name
self.age = age
self.gender = gender
self.course = course
# return None # 在这个方法中不能有返回值,
# 类中的方法
def choose_course(self, courses):
self.course.append(courses)
调用类发生了几件事?
'''
1、产生一个空对象
2、产生一个空对象的名称空间
3、让对象指向类的名称空间
4、调用了Student.__dict__方法(空对象,'jacky Liu', 22, 'male'),将空对象和类所需要的值传到类里
5、执行__init__方法为对象增加属性,并将产生的名字放到空对象的名称空间里,得到一个初始化对象(有值的对象)
'''
定义对象,也叫实例化对象
# print(stu1.__dict__) --> {}
stu1 = Student(stu1, 'elijah', 18, 'male')
1、产生一个空对象
2、产生一个空对象的名称空间
3、让对象指向类的名称空间
4、调用了Student.__dict__方法(空对象,'jacky Liu', 22, 'male'),将空对象和类所需要的值传到类里
5、执行__init__方法为对象增加属性,并将产生的名字放到空对象的名称空间里,得到一个初始化对象(有值的对象)
而实际使用中,调用类实例化对象时,会自动将对象名给传入类,所以可以省略第一个参数
stu1 = Student('elijah', 18, 'male')
'''
内部就是字典添加值的方式
stu1.__dict__['name'] = 'elijah'
stu1.__dict__['age'] = 18
stu1.__dict__['gender'] = 'male'
'''
print(stu1.__dict__) --> {'name': 'elijah', 'age': 18, 'gender': 'male'}
五、类和对象的增删改查
# 属性的查找:
# 1. 类属性: 在类中写的属性就称为类属性
# 2. 对象属性:在对象自己的名称空间中的属性就是对象属性
# 类属性的查找
# 1. 查
# print(Student.school)
# 2. 增加
# Student.country = 'China'
# 3. 改
# Student.school = 'BJ'
# 4. 删除
# del Student.school
# print(Student.__dict__)
# {}
stu = Student('ly', 18, 'male') # 实例化对象, stu就是一个实例
# 对象属性
# 1. 查
# print(stu.name)
# print(stu.age)
# print(stu.gender)
# 2. 增
# stu.aaa = 'aaa'
#
# # 3. 改
# stu.name = 'bbb'
# 4. 删除
# del stu.name
# print(stu.__dict__)
# 类中的方法,类可以调用,对象也可以调用
# 类调用方法
# Student.choose_course(stu, 'python') # 类来调用,函数的参数有几个就要传几个
# print(Student.choose_course)
# 对象调用方法
# 类中的方法,类可以调用,对象也可以调用, 但是,推荐对象来调用,因为对象来调用,会把自己当成第一个参数传递给函数
stu.choose_course('python') # stu.choose_course(stu, 'python')
class Teacher():
pass
print(isinstance(123, int))
print(isinstance(stu, Teacher))
类和对象属性的查找顺序:
定义类执行后,类会产生一个名称空间
实例化对象,每实例化一个对象都会单独产生一个属于该对象的名称空间
对象对它的属性进行增删改查操作并不能改变类中的属性,只能改对象本身名称空间中的属性
先从对象中找,再到类中找
注意:类中的方法,类可以调用,对象也可以调用, 但是,推荐对象来调用,因为对象来调用,会把自己当成第一个参数传递给函数
六、绑定方法
绑定方法分两类,一种绑定给对象,一种绑定给类
1、绑定给对象
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
def tell_info(self):
print('学生名字:%s 年龄:%s' % (self.name, self.age))
stu1 = Student('elijah', 18)
stu1.tell_info()
'''绑定给对象,对象名会自动被当作第一个参数传入方法函数中'''
2、绑定给类(需要用到装饰器@classmethod)
# 只要有 ip + port(端口) 就可以准确定位到一台电脑的某一个应用程序APP
class Mysql():
def __init__(self, ip, port):
self.ip = ip
self.port = port
@classmethod
def get_obj(cls):
obj = cls('127.0.0.1', 3306) # obj = self.__class__(settings.ip, setting.port)
return obj
obj = Mysql.get_obj()
print(obj.ip)
print(obj.port)
@classmethod:
一般调用类中的方法函数,都会自动将对象名当作第一个参数传入函数
使用类调用方法时,如果加上装饰器@classmethod,可以自动将类名当作第一个参数传入方法中
七、非绑定方法
# 静态方法 @staticmethod
import random
class Mysql():
def __init__(self, ip, port):
self.ip = ip
self.port = port
def get_num(self):
num = random.randint(1, 9)
print(num)
'''
像上面这种,get_num方法里的函数体代码根本用不着自动传进来的对象名self
就可以使用静态方法,去掉把对象名自动传入的功能
'''
import random
class Mysql():
def __init__(self, ip, port):
self.ip = ip
self.port = port
@staticmethod
def get_num():
num = random.randint(1, 9)
print(num)
obj = Mysql('127.0.0.1', 3306)
obj.get_num()
八、隐藏属性
1、为何要隐藏属性
在类中有一些属性是不希望能从外面直接进行修改的,把这种属性隐藏起来
如果要修改这类属性,在类中定义一些修改名字的方法,这样可以添加上一些限制条件
隐藏属性对外不对内,在类中可以进行修改(其实在外部也可以修改,但是不建议)
'''
1. 在类定义阶段,发生了语法上的变形_类名__属性名
2. 隐藏对外不对内
3. 只有在类定义阶段发生变形,其他情况都不发生变形了
为什么要隐藏: 类里面的隐藏属性, 类外部可以使用,但是目的不是让类外部使用的,类外部要是想用,在类内部开放接口进行访问
可以达到对外部数据的严格控制
'''
2、怎么样隐藏属性
语法: __属性名
class Student():
__school = 'oldboy' # _Student__school = 'oldboy'
def __init__(self, name, age):
self.name = name
self.age = age
# 直接查询school无法成功
stu1 = Student('elijah', 18)
print(stu1.school) # 直接报错
# 使用__school的方法也无法查询成功
print(stu1.__school)
# 得使用_Student__school 的方式查询,因为隐藏属性的实际形式是:_类名__属性名,但不建议在外部查询隐藏属性
print(stu1._Student__school) # oldboy
print(Student.__dict__)
# # {'__module__': '__main__', '_Student__school': 'oldboy', '__init__': , '__dict__': , '__weakref__': , '__doc__': None}
3、对隐藏属性进行修改
class Student():
__school = 'oldboy'
def __init__(self, name, age):
self.name = name
self.age = age
def check_school(self):
print(self.__school) # print(_Student__school)
# 在类体内定义一个修改隐藏属性的方法
def change_school(self, new):
if not isinstance(new, str):
print('学校名必须为字符串')
return
self.__school = new
print('修改成功!')
stu1 = Student('elijah', 18)
stu1.check_school() # oldboy
stu1.change_school('SH_oldboy') # 修改成功!
stu1.change_school(123) # 学校名必须为字符串
stu1.check_school() # SH_oldboy
九、property装饰器
可以让对象在调用方法时不用再添加括号
class Student():
__name = 'elijah'
# 定义函数供对象查看类中的隐藏属性
def check_name(self):
try:
print(self.__name)
except Exception:
print('没有该属性')
def change_name(self, new):
if not isinstance(new, str):
print('名字必须是字符串')
return
self.__name = new
print('修改成功!') # 只是在对象自己的独立空间中新创了变量名__name = new
def del_name(self):
try:
del self.__name
print('删除成功')
except Exception:
print('对象不可以删除类中的属性')
stu1 = Student()
stu1.check_name() # elijah
stu1.change_name('jason') # 修改成功!只是在对象自己的独立空间中新创了变量名__name = new
stu1.check_name() # jason,对象自己名称空间中的属性
stu1.del_name() # 删除成功, 删除了对象独立空间中的属性
stu1.check_name() # elijah, 对象独立空间中没有__name,从类中拿到的
property的使用
class Student():
__name = 'elijah'
# 定义函数供对象查看类中的隐藏属性
@property
def name(self):
print(self.__name)
# 当对象进行改操作时会自动触发, 而且方法名要与property下的方法名一致
@name.setter
def name(self, new):
if not isinstance(new, str):
print('名字必须是字符串')
return
self.__name = new
print('修改成功!')
# 当对象进行删除操作时触发, 而且方法名要与property下的方法名一致
@name.deleter
def name(self):
print('不让删')
stu1 = Student()
stu1.name = 123 # 名字必须是字符串
stu1.name = 'jason' # 修改成功!
stu1.name # jason
del stu1.name # 不让删
了解:
class Student():
__name = 'elijah'
# 定义函数供对象查看类中的隐藏属性
def check_name(self):
print(self.__name)
def change_name(self, new):
if not isinstance(new, str):
print('名字必须是字符串')
return
self.__name = new
print('修改成功!')
def del_name(self):
del __name
name = property(self.check_name, self.change_name, self.del_name)
stu1 = Student()
print(stu1.name)