列表:数据封装、函数:语句层面的封装、对象:数据、函数的封装
类:种类,将一类具有相同特性(抽象的)和动作的事物整合到一起就是类。
基于类而创建的一个具体的事物(具体存在),也是特征和动作整合到一起。
现实世界是先有对象再有类,程序中先定义类,后再有对象
面向对象设计 OOD(object oriented design):将一类具体事务的数据和动作整合到一起
面向对象编程 OOP(object-oriented programming):用定义类+实例/对象的方式去实现面向对象的设计
实例化:由类抽象对象的过程实例化
不是只有用 class 定义类才是面向对象,def 定义函数就是函数相关,跟面向对象没有关系:
面向对象设计:
def school(name,addr,type):def init(name, addr, type):sch = {'name': name,'addr': addr,'type': type,'kao_shi': kao_shi,'zhao_sheng': zhao_sheng,}return schdef kao_shi(school):print('%s 学校正在考试' %school['name'])def zhao_sheng(school):print('%s %s 正在招生' %(school['type'],school['name']))return init(name,addr,type)
s1 = school('清华大学', '中关村北大街', '公立学校')
print(s1)
print(s1['name'])
s1['kao_shi'](s1)
{'name': '清华大学', 'addr': '中关村北大街', 'type': '公立学校', 'kao_shi': .kao_shi at 0x0000000005382D90>, 'zhao_sheng': .zhao_sheng at 0x0000000005382BF8>}
清华大学
清华大学 学校正在考试
面向对象编程:
class School:def __init__(self, name, addr, types):self.name = nameself.addr = addrself.types = typesdef kao_shi(self):print('%s 正在考试' % self.name)def zhao_sheng(self):print('%s %s 正在招生' %(self.types, self.name))s1 = School('清华大学', '中关村北大街', '公立学校')
s1.kao_shi()
print(s1.__dict__)
清华大学 正在考试
{'name': '清华大学', 'addr': '中关村北大街', 'types': '公立学校'}
总结:
Python 是一门面向对象的语言,但是它不强制要求必须使用面向对象去设计代码,它不像 Java 一样万物皆是类。
类声明
class 类名:'类体字符串'类体def func():'函数文档字符串'函数体
self
如果把对象比作一个房子,那么 self 就是门牌号,有了 self 就可以轻松找到自己的房子。
当一个对象的方法被调用时,对象会将 自身的引用作为第一个参数传给该方法,那么 Python 就知道需要操作哪个对象的方法了。
class Students:def __init__(self, name, score):self.name = nameself.score = scoredef print_score(self):print('%s 分数是:%s' % (self.name, self.score))
s1 = Students('rose', 98) # 相当于 s1 = Stuents.__init__(s1, 'rose', 98)
s1.print_score()rose 分数是:98
构造方法
通常把 __init__() 方法称为构造方法,只有实例化一个对象,这个方法就会在对象被创建时自动调用。实例化传入的参数自动传入 __init__() 中,可以通过重写这个方法来自定义对象的初始化操作:
class Students:def __init__(self, name):self.name = namedef print_score(self):print('名字 %s' % self.name)
s1 = Students('rose') # 自动调用 __init__(),参数自动传入 __init__() 中
s1.print_score()
实例属性
对象只有数据属性,没有函数属性,用的时候找类要:
# s1.__dict__ 查看对象属性
class Students:color = 'black'def __init__(self, name, score):self.name = nameself.score = scoredef print_score(self):print('%s 分数是:%s' % (self.name, self.score))
s1 = Students('rose', 98)
print(s1.__dict__) # 查看对象属性字典,发现只有属性属性
print(id(Students.color)) # 对比发现类和对象的数据属性共享的
print(id(s1.color))
print(Students.print_score) # 发现类的函数属性与实例的函数属性不一样,内存地址不一样
print(s1.print_score)
{'name': 'rose', 'score': 98}
82934784
82934784
>
# bound method :绑定方法(绑定了 类 Students 的 print_score 方法)
可以看出对象只有数据属性没有函数属性,类的函数属性与实例的函数属性不一样,内存地址不一样。
类属性又称为静态变量,或静态数据,这些数据与它们所属的类对象绑定,不依赖任何实例。如果是 java 程序员,这种类型数据相当于在一个变量声明前加上 static 关键字
类名:大写、函数属性:动词+名词
数据属性
class Chinese:country = 'China' # 数据属性def __init__(self, name):self.name = namedef play_ball(self, ball):print('%s 正在打 %s' % (self.name))
p1 = Chinese('rose')
# 查看类属性
print(Chinese.country)
# 修改
Chinese.country = 'CHINA'
print(Chinese.country)
# 增加
Chinese.gender = 'male'
print(Chinese.__dict__)
# 删除
del Chinese.gender
print(Chinese.__dict__)
China
CHINA
{'__module__': '__main__', 'country': 'CHINA', '__init__': , 'play_ball': , '__dict__': , '__weakref__': , '__doc__': None, 'gender': 'male'}
{'__module__': '__main__', 'country': 'CHINA', '__init__': , 'play_ball': , '__dict__': , '__weakref__': , '__doc__': None}
函数属性
# 增加一个 eat 方法
def eat(self, food):print('%s 正在吃 %s' % (self.name, food))Chinese.test = eat # test 指向 eat
p1.test('fish')
print(Chinese.__dict__)
# Chinese.play_ball = ball 修改属性
rose 正在吃 fish
{'__module__': '__main__', 'country': 'China', '__init__': , 'play_ball': , '__dict__': , '__weakref__': , '__doc__': None, 'test': }
class Students:color = 'black'def __init__(self, name, score):self.name = nameself.score = scoredef print_score(self):print('%s 分数是:%s' % (self.name, self.score))
s1 = Students('rose', 98)
# 查看数据、函数属性
print(s1.__dict__)
print(s1.name)
print(s1.print_score) # > 可以看出实例的函数属性绑定的是 类的函数属性(
{'name': 'rose', 'score': 98}
rose
>
{'name': 'rose', 'score': 98, 'age': 18}
{'name': 'rose', 'score': 98, 'age': 20}
{'name': 'rose', 'score': 98}
属性作用域
在 class 类中定义的数据属性,属于类的属性。在 __init__() 中定义的是实例的数据属性。不管是类的还是实例的数据属性,都只能使用点(.)方式访问,其他方式访问皆为普通变量。
country = '中国》》》'
class Chinese:country = '中国'def __init__(self, name):self.name = nameprint('---->', country) # 这里访问的就是普通变量,没有选择类的属性 country = '中国',而是选择了普通变量 country = '中国》》》'def eat(self):pass
p1 = Chinese('林雷')
# p1.country = '》》》中国' # 其实这里是增加实例的数据属性
# print('类的数据属性:', Chinese.country) 类的数据属性: 中国
# print('实例的数据属性:', p1.country) 实例的数据属性: 》》》中国print(Chinese.country)
print(p1.country)----> 中国》》》
中国
中国
总结
我们知道类既有数据属性也有函数属性,而实例只有数据属性。在使用实例调用类的函数属性时,总要带上函数后面的括号才能运行,不然调用的是函数的内存地址:
class Room:def __init__(self, name, owner, width, heigh, length):self.name = nameself.owner = ownerself.width = widthself.heigh = heighself.length = lengthdef calc_area(self):print('%s 住的%s,总面积:%s' % (self.owner, self.name, self.width * self.length))r1 = Room('卧室', 'rose', 10, 3.5, 16)
r1.calc_area # >
问题:那么有什么方法可以向调用数据属性一样调用函数属性呢?
类中提供了 @property 关键字,它的作用是在调用函数属性时,可以像调用数据属性一样并允许该函数,类似于函数中的装饰器。
class Room:def __init__(self, name, owner, width, heigh, length):self.name = nameself.owner = ownerself.width = widthself.heigh = heighself.length = length@property def calc_area(self):print('%s 住的%s,总面积:%s' % (self.owner, self.name, self.width * self.length))
# return self.width * self.length
r1 = Room('卧室', 'rose', 10, 3.5, 16)
# r1.calc_area()
r1.calc_area # 省略括号
只需在需要被装饰的函数上面,加上 @property 即可。需要注意的是:被装饰的函数不能有除 self 参数以外的参数
如果不进行实例化,直接用类名调用类的函数属性,会报缺少位置参数:
class Room:def __init__(self, name, owner, width, heigh, length):self.name = nameself.owner = ownerself.width = widthself.heigh = heighself.length = length@propertydef calc_area(self):print('%s 住的%s,总面积:%s' % (self.owner, self.name, self.width * self.length))def open_door(self):print('打开%s的门' % self.name)Room.open_door()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()13 print('打开%s的门' % self.name)14
---> 15 Room.open()TypeError: open_door() missing 1 required positional argument: 'self'
传入 self 做参数,也会报 name 'self' is not defined。这是因为 self 是特殊意义的,是实例本身,只有在实例化时才有效。为了解决这种问题,我们引入了 @classmethod ,:
class Room:size = 'small'def __init__(self, name, owner, width, heigh, length):self.name = nameself.owner = ownerself.width = widthself.heigh = heighself.length = length@propertydef calc_area(self):print('%s 住的%s,总面积:%s' % (self.owner, self.name, self.width * self.length))@classmethoddef open_door(cls, color):print(cls) # cls 相当于 类本身,在定义类方法时自动补全print('请打开那扇 %s 的 %s门' % (color, cls.size))Room.open_door('red')---------------------------------------------------
请打开那扇 red 的 small门
可以看出一旦类的某个函数属性被定义为类方法时,可以直接使用类名调用类的函数属性,而不用担心参数的问题。在定义类方法时,Python 会自动补全一个名叫 cls 的参数,可以看出它就是类本身,在调用类函数属性时不需要手动传入,本身会自动传入,与 self 类似。
如果有想在类中定义一个函数,要求该函数的参数与类、实例都无关(没有绑定),我们可以使用静态方法 @staticmethod:
class Room:size = 'small'def __init__(self, name, owner, width, heigh, length):self.name = nameself.owner = ownerself.width = widthself.heigh = heighself.length = length@staticmethoddef close_door(position, color):print('请关闭 %s 的 %s 小门' % (position, color))r1 = Room('卧室', 'rose', 10, 20, 30)
Room.close_door('厨房', 'black')
r1.close_door('厕所', 'white')
被定义为静态方法的函数属性,名义上归类管理,但不能使用类变量和实例变量,只是类的工具包。
class Room:size = 'small'def __init__(self, name, owner, width, heigh, length):self.name = nameself.owner = ownerself.width = widthself.heigh = heighself.length = length@propertydef calc_area(self):print('%s 住的%s,总面积:%s' % (self.owner, self.name, self.width * self.length))@classmethoddef open_door(cls, color):#print(cls)print('请打开那扇 %s 的 %s门' % (color, cls.size))@staticmethoddef close_door(position, color):print('请关闭 %s 的 %s 小门' % (position, color))def test(a, b):print(a, '-->', 'b')r1 = Room('卧室', 'rose', 10, 20, 30)
# 静态属性
Room.calc_area # 不能调用
r1.calc_area # rose 住的卧室,总面积:300# 类方法
Room.open_door('white') # 请打开那扇 white 的 small门
r1.open_door('white') # 请打开那扇 white 的 small门# 静态方法
Room.close_door('卧室', 'black') # 请关闭 卧室 的 black 小门
r1.close_door('卧室', 'black') # 请关闭 卧室 的 black 小门# 常规函数
Room.test(1, 2) # 1 --> b
r1.test(1, 2) # 不能调用
组合就是把一个类实例化传入另一个类中使用。类与类之间相互关联,并且小的类组成大的类,这个时候可以用组合。
class Turtle:def __init__(self, x):self.x = x
class Fish:def __init__(self, x):self.x = xclass Pool:def __init__(self, x, y):self.tutle = Turtle(x)self.fish = Fish(y)def print_num(self):print('水池里总共有%s只乌龟,%s条鱼' % (self.tutle.x, self.fish.x))p1 = Pool(2, 3)
p1.print_num()
水池里总共有2只乌龟,3条鱼
选课系统:
class School:def __init__(self, name, addr):self.name = nameself.addr = addrdef zhao_sheng(self):print('%s正在招生' % self.name)class Course:def __init__(self, name, price, period, school):self.name = nameself.price = priceself.period = periodself.school = schoolclass Teacher:def __init__(self, name, gender, age):self.name = nameself.gender = genderself.age = ages1 = School('oldboy', '北京')
s2 = School('oldboy', '南京')
s3 = School('oldboy', '东京')t1 = Teacher('林老师', '男', '45')
t2 = Teacher('李老师', '女', '28')
t3 = Teacher('孙老师', '女', '32')c1 = Course('linux', '10000', '1', s1)
print(c1.__dict__)msg = """
1 老男孩 北京校区
2 老男孩 南京校区
3 老男孩 东京校区
4 林老师
5 李老师
6 孙老师
"""while True:print(msg)menu = {'1': s1,'2': s2,'3': s3,'4': t1,'5': t2,'6': t3}choice = input('请选择校区:')choice_teacher = input('请选择老师:')teacher_obj = menu[choice_teacher]school_obj = menu[choice]name = input('课程名:')price = input('课程费用:')period = input('课程周期:')new_course = Course(name, price, period, school_obj) # school_obj = s1 print('课程【%s】属于【%s】学校' % (new_course.name, new_course.school.name)) # new_courese.school = s1 s1.name = name = oldboynew_teacher = Course(name, price, period, teacher_obj)print('%s由%s教' % (new_teacher.name, new_teacher.school.name))