Python 面向对象
Python之路 Day6 面向对象
面向过程VS面向对象
面向过程编程
面向过程编就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。
举个典型的面向过程的例子, 数据库备份, 分三步,连接数据库,备份数据库,测试备份文件可用性。
面向对象编程
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
Class类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object对象
一个对象即是一个类的实例化后实例,一个类需要经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承,类的继承会继承构造函数。
自定义构造函数后还想继承类的构造函数方法,'super(子类名,self)._init_(基类的参数)',在子类中调用父类的方法, super().sleep()或者People.sleep(self)。多继承时,继承了第一个父类的构造函数后就不会在继承第二个父类的构造函数。
继承多父类(从左到右的查询),查询方法有两种,第一种时广度优先(python3默认):先将父类的查询完,
第二张是深度优先:如果第一个父类没有对应的方法,就接着查询第一个父类的父类有没有对应的方法。
在python2中经典类是深度优先,新式类是广度优先。
例子:
import time
class School(object):
def __init__(self, name, addr):
self.name = name
self.addr = addr
def time_info(self):
return time.strftime('%Y-%m-%d %X', time.localtime(time.time()))
class Teacher(School):
def __init__(self, name, age, addr, salary, course):
super(Teacher, self).__init__(name,addr) # 在父类的基础上添加了新的属性
self.salary = salary
self.course = course
def a(self):
print('当前时间是: ',super().time_info())
zyl=Teacher('zyl',22,'杭州','100','Py')
zyl.a()
Polymorphism多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
例子:
class Animal(object):
def __init__(self, name): # Constructor of the class
self.name = name
@staticmethod
def animal_talk(obj):
obj.talk()
class Cat(Animal):
def talk(self):
print('Meow!')
class Dog(Animal):
def talk(self):
print('Woof! Woof!')
d = Dog("冰冰")
c = Cat("浩浩")
Animal.animal_talk(c) #通过统一的端口转入不同的值,实现多种效果。
Animal.animal_talk(d)
注意信息:
1.多个对象会公用类的方法。
2.实例修改类变量,其实是没有修改,而是在实例内部生成了新的变量,因为实例内部是一个作用域。
类的其他属性
A:静态变量,静态变量属于类,既可以使用类访问,也可以使用对象访问.
例:class School(object):
a=1 #静态变量是存储在类的内存空间中的
def __init__(self, addr):
self.addr = addr
bj=School('杭州')
print(bj.a)
print(School.a)
B:动态变量只属于对象,只能由对象访问。
例:class School(object):
a=1
def __init__(self, addr):
self.addr = addr
bj=School('杭州')
print(bj.addr)
print(School.addr) #打印时会报错的,无法通过类来访问
C:类的属性 在方法前面加入@property装饰器,这个方法就变成了类的属性,无需加()即可调用。
例:class School(object):
def __init__(self, addr):
self.addr = addr
@property
def time_info(self):
print(time.time())
bj=School('杭州')
bj.time_info
D:私有变量 在变量前面加入两个下划线就将这个变量变为了私有变量,创建的对象没有办法访问私有变量,私有变量只能内部访问。但是怎么样才能访问呢?换个思路,我们给它一个提示,让它将结果返回给我们即可。
例:class Cae:
def __init__(self,name,colour):
self.__name = name
@property
def ss(self):
return self.__name
c1=Cae('name','yellow')
print(c1.ss)
E:私有方法:方法前面加上两个下划线就会变成私有方法,而如果要访问这个方法只能在类的内部才可以访问。外部如果想要访问,需要通过别的实例引用。
例: class Cae:
def __init__(self,name,colour):
self.name=name
self.colour=colour
def __show(self):
print("The car's name is %s and colour is %s." % (self.name, self.colour))
def ss(self):
self.__show()
c1 = Cae('name', 'yellow')
c1.ss() 或 c1._Cae__show()
F:私有变量更改,
例:class Cae:
def __init__(self,name,colour):
self.__name = name
self.__colour = colour
print("我被创建了")
def show(self):
print("The car's name is %s and colour is %s." % (self.__name, self.__colour))
@property ##先设置为属性,才可更改
def ss(self):
self.__name
@ss.setter ##更改变量值
def ss(self,value):
self.__name=value
c1=Cae('name','yellow')
c1.show()
c1.ss='SYF' ##上传一个值
c1.show()
J:析构函数:__init__()在对象被创建的时候进行调用,成为构造函数。而对象被销毁的时候或程序运行失败后就会调用__del__()函数,成为析构函数。
例:class Car(object):
def __init__(self):
print('我被创建了')
def __del__(self):
print('我要被销毁了')
c1=Car()
c2=Car()
del c1
time.sleep(10)
H: __call__方法,把对象作为一个函数进行执行的时候,就会执行这个方法。
例:class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __call__(self, friend):
print('My name is %s...' % self.name)
print('My friend is %s...' % friend)
p = Person('Bob', 'male')
p('Tim')
I: 静态方法:@staticmethod,在类中使用,创建一个函数,而不是方法。方法变为静态方法后,无法访问类或实例中的任何属性,多个静态方法的唯一关联就是都是通过类来调用的。
例:class test(object):
age=22
def __init__(self,name):
self.name=name
@staticmethod
def print_name():
print(age) #此处会报错
print(self.name) #此处会报错
test=test('zyl')
test.print_name()
J: 类方法:@classmethod,只能访问类变量,不能访问实例变量。应用场景是对象不能重新声明类变量,只能去访问类变量。
例:class test(object):
name='wq'
def __init__(self,name):
self.name=name
@classmethod
def print_name(self):
print(self.name) #此处打印的结果为‘wq’
test=test('zyl')
test.print_name()
K: .__getitem__、__setitem__、__delitem__ 用于索引操作,如字典。以上分别表示获取、设置、删除数据,就是将对像封装成字典,在对字典操作时可以进行一些判断。
例:class Cae(object):
def __init__(self):
self.data={}
def __getitem__(self, item):
print('__get__',item)
if item not in self.data:
print('数据不存在')
else:
print(self.data[item])
def __setitem__(self, key, value):
print('__get__',key,value)
self.data[key]=value
def __delitem__(self, key):
if key == 'a':
print('不可以删除')
else:
del self.data[key]
a=Cae()
a['a']=1
a['b']=2
del a['a']
del a['b']
a['a']
a['b']
L: __doc__ 打印类的描述信息
例:class Foo:
""" 描述类信息,这是用于看片的神奇 """
def func(self):
pass
print Foo.__doc__
#输出:类的描述信息
M:__module__ 和 __class__
__module__ 表示当前操作的对象在那个模块,如果是当前文件则返回__main__
__class__ 表示当前操作的对象的导入路径.类名,如果是当前文件则返回__main__.类名
N: 查看类或对象中的所有成员
例:class Province(object):
country = 'China'
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print('func')
# 获取类的成员,即:静态字段、方法、
print(Province.__dict__)
# 输出:{'__module__': '__main__', 'country': 'China', '__init__': <function Province.__init__ at 0x0000024B7BA38730>, 'func': <function Province.func at 0x0000024B7BA387B8>, '__dict__': <attribute '__dict__' of 'Province' objects>, '__weakref__': <attribute '__weakref__' of 'Province' objects>, '__doc__': None}
# 获取 对象obj1 的成员
obj1 = Province('HeBei',10000)
print(obj1.__dict__)
# 输出:{'count': 10000, 'name': 'HeBei'}
O: __str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
例:class Foo:
def __str__(self):
return 'zyl'
obj = Foo()
print(obj)
# 输出:zyl
P: __new__ #实例化类是通过__new__方法实现的,__new__里面调用了__init__,new方法先于init方法,用于实例化之前进行定制。如果你要重构父类的new方法,在最后必须继承父类的new方法`return object.__new__(cls)`
例:class test(object):
def __init__(self):
print('init')
def __new__(cls, *args, **kwargs):
print('new')
return object.__new__(cls) #cls相当于test
a=test()
# 输出:
new
init
Q: 生成类的其他方式
def func(self):
print('hello word %s' %self.name)
def a(self,name):
self.name=name
Foo=type('Foo',(object,),{'f':func,'__init__':a})
f=Foo('zyl')
f.f()
R: 反射,通过字符串,映射或修改程序运行时的状态、属性、方法, 有以下4个方法
hasattr: 判断字符串是否是类中的一个属性、方法
getattr: 返回字符串对应类中属性的结果、方法的内存对象
setattr: 给类中添加一个属性
delattr: 删除类中的某个属性
示例:
class a(object):
def __init__(self,name):
self.name=name
def printName(self):
print('My name is %s' %self.name)
d=a('zyl')
choice=input('>>: ').strip()
if hasattr(d,choice):
print(getattr(d,choice))
else:
setattr(d,choice,'22')
print(getattr(d,choice))
delattr(d,choice)
if not hasattr(d,choice):
print('删除成功')
#注:创建的所有类都相当于是type的实例
基础补充
A:异常处理:
try/except/else语句 ##类似于if then else语句
语法:
try:
<语句> #运行别的代码
except <某种错误>:
<语句> #如果在try部份引发了'某种错误'异常
except <某种错误> as <数据>:
<语句> #如果引发了'某种错误'异常,获得附加的数据
except (<某种错误,<某种错误>)
<语句> #如果引发了其中的某个异常
except Exception:
<语句> #所有错误都会出发
else:
<语句> #如果没有异常发生
finally:
<语句> #不管有没有错误都执行这段
#注:try,except区块是一个作用域,在里面设置的变量,在外部无法调用
B:自定义异常: raise
示例:
class zylerror(Exception):
def __init__(self,msg):
pass
def __str__(self): #str才是真正返回数据的方法,不写的话返回6666.
return 'aa'
try:
raise zylerror('6666')
except zylerror as e:
print(e)
#输出:aa
C:动态导入模块,根据字符串来导入模块
方法一:
__import__('lib.test',fromlist=(' ')) #可以写字符串,不写fromlist只会返回lib,而fromlist为“空”是返回test模块。
方法二:
import importlib
mod=importlib.import_module('lib.test')
D:断言,断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。
示例:
assert type(‘123’) is str
print(1) #断言成功执行此语句,不成功则退出程序。
每日练习:
角色:学校、学员、课程、讲师
要求:
1. 创建北京、上海 2 所学校
2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开
3. 课程包含,周期,价格,通过学校创建课程
4. 通过学校创建班级, 班级关联课程、讲师
5. 创建学员时,选择学校,关联班级
5. 创建讲师角色时要关联学校,
6. 提供两个角色接口
6.1 学员视图, 可以注册, 交学费, 选择班级,
6.2 讲师视图, 讲师可管理自己的班级, 上课时选择班级, 查看班级学员列表 , 修改所管理的学员的成绩
6.3 管理视图,创建讲师, 创建班级,创建课程
7. 上面的操作产生的数据都通过pickle序列化保存到文件里
代码如下:
school类模块文件
# Author:ZhuYuLiang
from prettytable import PrettyTable
import re
class School(object):
def __init__(self,name,address):
'''
初始化学校的名称,地址,主要课程,班级。
:param name:
:param address:
'''
self.name=name
self.address=address
self.course={}
self.class_name={}
self.teacher_name={}
self.student={}
print('''
创建成功,学校信息如下:
学校名称: {}
学校地址: {}
'''.format(self.name,self.address,))
def create_course(self,name,cycle,price):
'''
采用字典格式,创建课程。课程的信息有周期和价格
:param name:
:param cycle:
:param price:
:return:
'''
self.course[name]=[cycle,price]
print('创建{}课程成功。'.format(name))
def create_class(self,class_name,course_name,teacher_name):
'''
采用字典格式,创建班级。班级的信息有主要课程和讲课老师
:param class_name:
:param course_name:
:param teacher_name:
:return:
'''
self.class_name[class_name]=[course_name,teacher_name,[]]
self.teacher_name[teacher_name][1].append(class_name)
print('创建{}班级成功!'.format(class_name))
def create_teacher(self,name,course):
self.teacher_name[name]=[course,[]]
print('创建{}讲师成功,擅长的课程是: {}'.format(name,course))
print(self.teacher_name.keys())
def show_info(self,i,name):
print(name.center(30, '*'))
if name == 'teacher':
x = PrettyTable(['name', 'course'])
for info in i.keys():
x.add_row([info, i[info][0]])
print(x)
elif name == 'grade':
x=PrettyTable(['class_name','course_name','teacher_name'])
for info in i.keys():
x.add_row([info,i[info][0], i[info][1]])
print(x)
elif name == 'student':
x=PrettyTable(['name','pay','class'])
for info in i.keys():
x.add_row([i[info][0].name,i[info][1], i[info][2]])
print(x)
else:
x=PrettyTable(['name','cycle','price'])
for info in i.keys():
x.add_row([info, i[info][0], i[info][1]])
print(x)
def set_question(self,name):
while True:
class_name=input('要给哪个班级出题: ')
if class_name not in self.teacher_name[name][1]:
print('输入错误,您没有\033[31;1m{}\033[0m'.format(class_name))
continue
else:
question = input('请出题(以逗号分割多道题): ')
question_list = re.split(',', question)
self.class_name[class_name].append(question_list)
break
def modifi_score(self,name):
while True:
info=input('您好,{}老师,请问你要修改哪个班级下的学员(空格分割): '.format(name))
if len(info.split(' ')) != 2:
print('参数必须为2位: ')
continue
else:
class_name=info.split(' ')[0]
student_name=info.split(' ')[1]
if class_name not in self.class_name or name != self.class_name[class_name][1]:
print('班级名称错误,或者你不是该班班主任!')
continue
elif student_name not in self.class_name[class_name][2]:
print('没有这位学生')
continue
else:
score=input('输入分数: ')
if not score.isdigit() or int(score) <0:
print('修改错误,分数必须为数字且大于零!')
continue
elif len(self.student[student_name]) < 4:
print('该学生还没有答过题!')
continue
else:
self.student[student_name][-1]=score
break
class Student(object):
def __init__(self,name,obj):
self.name=name
obj.student[name]=[self]
print('{}同学注册成功'.format(self.name))
def pay(self,name,obj):
obj.student[name].append(True)
print('交费成功')
def Choosing_classes(self,name,class_name,obj):
if len(obj.student[name]) <2:
print('你还没有缴费请先缴费')
else:
obj.class_name[class_name][2].append(name)
obj.student[name].append(class_name)
print('选择{}班级成功'.format(class_name))
main主运行文件(数据保存在上级db目录下)
# Author:ZhuYuLiang
import school
import shelve
import os
import re
from prettytable import PrettyTable
menu={'学员视图':['注册','交学费','选择班级','答题','查看成绩'],\
'讲师视图':['查看班级','查看学员列表','出题','修改学员成绩'],\
'管理视图':['创建讲师','创建班级','创建课程','查看讲师','查看班级','查看课程','查看学员']}
def print_menu(*args,**kwargs):
for index, i in enumerate(eval(args[0])):
print('{}. {}'.format(index, i))
choice=input('请选择视图: ')
return choice
def run():
info = shelve.open('../db/info.txt')
bj_oldboy=info.get('bj_oldboy')
sh_oldboy=info.get('sh_oldboy')
if not bj_oldboy or not sh_oldboy:
bj_oldboy=school.School('bj_oldboy','北京')
sh_oldboy=school.School('sh_oldboy','上海')
print('\033[31;1m初始化学校成功\033[0m')
school_list=[bj_oldboy,sh_oldboy]
while True:
for index,obj in enumerate(school_list):
print('{}. {}'.format(index,obj.name))
choice=input('请选择学校: ')
if choice not in ['0','1']:
continue
else:
school_obj=school_list[int(choice)]
break
while True:
choice_1=print_menu('menu.keys()')
if choice_1 == '0':
while True:
choice_2=print_menu("menu['学员视图']")
if choice_2 == 'q':break
name = input('输入姓名: ')
if choice_2 == '0':
student=school.Student(name,school_obj)
elif choice_2 == '1':
if name in school_obj.student:
if len(school_obj.student[name]) > 3:
print('你还没有选择班级')
continue
else:
school_obj.student[name][0].pay(name,school_obj)
else:
print('你还没有注册')
continue
elif choice_2 == '2':
if name in school_obj.student:
while True:
school_obj.show_info(school_obj.class_name, 'grade')
choice_3 = input('请选择班级: ')
if choice_3 in school_obj.class_name:
school_obj.student[name][0].Choosing_classes(name,choice_3,school_obj)
break
else:
print('选择错误,请重新输入!')
continue
else:
print('你还没有注册')
continue
elif choice_2 == '3':
if name in school_obj.student:
class_name=school_obj.student[name][2]
if len(school_obj.class_name[class_name]) <4:
print('你的班级暂时没有作业')
else:
score = 0
count=1
for line in school_obj.class_name[class_name][-1]:
answer=input('第{}题:{}= '.format(count,line))
if int(answer) == eval(line):
score+=10
print('答对了,加10分')
else:
print('不好意思,答错了')
count+=1
school_obj.student[name].append(score)
else:
print('你还没有注册')
continue
elif choice_2 == '4':
if name in school_obj.student:
class_name = school_obj.student[name][2]
if len(school_obj.student[name]) <4:
print('你还没有答过题呢。')
else:
print('你的分数是: {}'.format(school_obj.student[name][-1]))
else:
print('你还没有注册')
continue
else:
print('\033[31;1m输入有误请重新输入!\033[0m')
continue
elif choice_1 == '1':
judge=True
while judge:
name = input('输入姓名: ')
if name not in school_obj.teacher_name:
print('\033[31;1m名称输入错误!\033[0m')
continue
while judge:
choice_2=print_menu("menu['讲师视图']")
if choice_2 == '0':
print('你管理的班级如下: ')
x=PrettyTable(['班级名称'])
for i in school_obj.teacher_name[name][1]:
x.add_row([i])
print(x)
elif choice_2 == '1':
x = PrettyTable(['班级名称', '学员名称'])
for i in school_obj.teacher_name[name][1][:]:
user=re.sub('[\[\]\']','',str(school_obj.class_name[i][2][:]))
x.add_row([i,user])
print(x)
elif choice_2 == '2':
school_obj.set_question(name)
elif choice_2 == '3':
school_obj.modifi_score(name)
elif choice_2 == 'q':
judge=False
else:
print('\033[31;1m输入有误请重新输入!\033[0m')
continue
elif choice_1 == '2':
while True:
choice_2=print_menu("menu['管理视图']")
if choice_2 == '0':
print('输入你要创建的讲师信息: ')
name=input('输入你的姓名: ')
course=input('输入你擅长的课程: ')
if course not in ['python','go','linux']:
print('您擅长的课程,学校暂时未开放,Sorry。')
break
else:
if course == 'go':
sh_oldboy.create_teacher(name,course)
else:
bj_oldboy.create_teacher(name,course)
elif choice_2 == '1':
print('输入你要创建的班级信息: ')
name=input('班级名称: ')
course_name=input('班级所授课程: ')
teacher_name=input('班级讲师: ')
if course_name not in school_obj.course or \
teacher_name not in school_obj.teacher_name:
print('课程或讲师不存在!')
continue
elif course_name != school_obj.teacher_name[teacher_name][0]:
print('该老师专业与此课程不符')
continue
else:
school_obj.create_class(name,course_name,teacher_name)
print(school_obj.class_name.keys())
elif choice_2 == '2':
while True:
print('输入你要创建的课程信息: ')
name=input('课程名称: ')
cycle=input('课程周期: ')
price=input('课程价格: ')
if name == 'linux' or name == 'python':
bj_oldboy.create_course(name,cycle,price)
break
elif name == 'go':
sh_oldboy.create_course(name, cycle, price)
break
else:
print('\033[31;1m输入有误请重新输入\033[0m')
continue
elif choice_2 == '3':
school_obj.show_info(school_obj.teacher_name,'teacher')
elif choice_2 == '4':
school_obj.show_info(school_obj.class_name,'grade')
elif choice_2 == '5':
school_obj.show_info(school_obj.course,'course')
elif choice_2 == '6':
school_obj.show_info(school_obj.student, 'student')
elif choice_2 == 'q':
break
else:
print('\033[31;1m输入有误请重新输入!\033[0m')
continue
elif choice_1 == 'q':
info=shelve.open('../db/info.txt')
info['bj_oldboy']=bj_oldboy
info['sh_oldboy']=sh_oldboy
info.close()
exit()
else:
print('\033[31;1m输入有误请重新输入!\033[0m')
continue
if __name__ == '__main__':
run()