Python学习之面向对象
http://www.cnblogs.com/linhaifeng/articles/6182264.html
http://www.runoob.com/python3/python3-class.html
https://blog.csdn.net/dreamhua927/article/details/52461816
https://blog.csdn.net/qq_31780525/article/details/72639491
面向对象技术简介
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。(把一类事物的相同的特征和动作整合到一起就是类I,类是一个抽象的概念)
- 方法:类中定义的函数。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 实例变量:定义在方法中的变量,只作用于当前实例的类。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。(就是基于类而创建的一一个具体的事物(具体存在的),也是特征和动作整合到一起)
和其它编程语言相比,Python 在尽可能不增加新的语法和语义的情况下加入了类机制。
Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。
对象可以包含任意数量和类型的数据。
面向对象重要的概念就是类(Class)和实例(Instance),类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。 先回顾下 OOP 的常用术语: 类:对具有相同数据和方法的一组对象的描述或定义。 对象:对象是一个类的实例。 实例(instance):一个对象的实例化实现。 实例属性(instance attribute):一个对象就是一组属性的集合。 实例方法(instance method):所有存取或者更新对象某个实例一条或者多条属性的函数的集合。 类属性(classattribute):属于一个类中所有对象的属性,不会只在某个实例上发生变化 类方法(classmethod):那些无须特定的对象实例就能够工作的从属于类的函数。
类定义
语法格式如下:
在Python中,定义类是通过class关键字,class后面紧接着是类名,类名规范是首字母大写如class Chinese init方法的第一个参数永远是self,表示创建的实例本身,因此,在init方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。 有了init方法,在创建实例的时候,就不能传入空的参数了,必须传入与init方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去,和普通的函数相比,在类中定义的对象函数(还有静态方法,类方法)只有一点不同,就是第一个参数永远是实例变量self,并且,调用时不用传递该参数。 ***对象属性 Python 中对象的属性包含对象的所有内容:方法和数据,注意方法也是对象的属性。查找对象的属性时,首先在对象的__dict__ 里面查找,然后是对象所属类的dict,再往后是继承体系中父类(MRO解析)的dict,任意一个地方查找到就终止查找,并且调用 __getattribute__(也有可能是__getattr__) 方法获得属性值。 ***方法 在 Python 类中有3种方法,即静态方法(staticmethod),类方法(classmethod)和实例 对于实例方法,在类里每次定义实例方法的时候都需要指定实例(该方法的第一个参数,名字约定成俗为self)。这是因为实例方法的调用离不开实例,我们必须给函数传递一个实例。假设对象a具有实例方法 foo(self, *args, **kwargs),那么调用的时候可以用 a.foo(*args, **kwargs),或者 A.foo(a, *args, **kwargs),在解释器看来它们是完全一样的。 类方法每次定义的时候需要指定类(该方法的第一个参数,名字约定成俗为cls),调用时和实例方法类似需要指定一个类。 静态方法其实和普通的方法一样,只不过在调用的时候需要使用类或者实例。之所以需要静态方法,是因为有时候需要将一组逻辑上相关的函数放在一个类里面,便于组织代码结构。一般如果一个方法不需要用到self,那么它就适合用作静态方法。 ***数据属性: 通过内建函数dir(),或者访问类的字典属性__dict__,这两种方式都可以查看类或者实例有哪些属性。对于类数据属性和实例数据属性,可以总结为: 类数据属性属于类本身,可以通过类名进行访问/修改; 类数据属性也可以被类的所有实例访问/修改; 在类定义之后,可以通过类名动态添加类数据属性,新增的类属性也被类和所有实例共有; 实例数据属性只能通过实例访问; 在实例生成后,还可以动态添加实例数据属性,但是这些实例数据属性只属于该实例;
类有两种属性:数据属性和函数属性
1. 类的数据属性是所有对象共享的
2. 类的函数属性是绑定给对象用的
查看类属性:dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值

特殊的类属性


class Music:
'这是一个音乐人的类'
dang='嘻哈'
def music():
print('good')
def listen(self):
print('nice')
#
# print(Music.dang)
# Chinese.music()
# Chinese.listen('哈哈')
#
# print(dir(Music)) #查出的是一个名字列表
# print(Music.__dict__) #查看属性字典
# print(Music.__dict__['dang']) #取出dang对应的value
# Music.__dict__['music']()
# Music.__dict__['listen'](1)
print(Music.__name__) #类名 : Music
print(Music.__doc__) #类的文档字符串 :这是一个音乐人的类
print(Music.__module__) #类所在的模块:__main__
1 #类属性增删改查 2 class Chinese: 3 country='China' 4 def __init__(self,name): 5 self.name=name 6 7 def play_ball(self,ball): 8 print('%s 正在打 %s' %(self.name)) 9 #查看 10 print(Chinese.country) #China 11 12 #修改 13 Chinese.country='Japan' 14 print(Chinese.country) #Japan 15 16 p1=Chinese('alex') 17 print(p1.__dict__) #{'name': 'alex'} 18 print(p1.country) #Japan 19 20 #增加 21 Chinese.dang='党' 22 print(Chinese.dang) #党 23 print(p1.dang) #党 24 25 #删除 26 del Chinese.dang 27 del Chinese.country 28 29 print(Chinese.__dict__) 30 print(Chinese.country) 31 32 33 def eat_food(self,food): 34 print('%s 正在吃%s' %(self.name,food)) 35 36 Chinese.eat=eat_food 37 38 print(Chinese.__dict__) 39 p1.eat('饭') #alex 正在吃饭 40 41 42 def test(self): 43 print('test') 44 45 Chinese.play_ball=test 46 p1.play_ball() # 相当于Chinese.play_ball(p1):test
1 class Chinese: 2 country='China' 3 def __init__(self,name): 4 self.name=name 5 6 def play_ball(self,ball): 7 print('%s 正在打 %s' %(self.name,ball)) 8 p1=Chinese('alex') 9 print(p1.__dict__) #{'name':alex} 10 11 #查看 12 print(p1.name) #alex 13 print(p1.play_ball) #<bound method Chinese.play_ball of <__main__.Chinese object at 0x0000006475C11EB8>> 14 15 #增加 16 p1.age=18 17 print(p1.__dict__) #{'name': 'alex', 'age': 18} 18 print(p1.age) #18 19 20 #不要修改底层的属性字典,虽可执行但不可取 21 # p1.__dict__['sex']='male' 22 # print(p1.__dict__) #{'name': 'alex', 'age': 18, 'sex': 'male'} 23 # print(p1.sex) #19 24 25 #修改 26 p1.age=19 27 print(p1.__dict__) #{'name': 'alex', 'age': 19} 28 print(p1.age) #19 29 30 #删除 31 del p1.age 32 print(p1.__dict__) #{'name': 'alex'}
对象与实例属性的三个例子:


init定义的都在实例字典里 class下定义的在类字典里
class Chinese:
country='China'
l=['a','b']
def __init__(self,name):
self.name=name
def play_ball(self,ball):
print('%s 正在打 %s' %(self.name,ball))
p1=Chinese('alex')
print(p1.l) --》['a', 'b']
p1.l=[1,2,3]
print(Chinese.l) --》['a', 'b']
print(p1.__dict__) --》{'name': 'alex', 'l': [1, 2, 3]}
p1.l.append('c') #这种增加值不会赋值到属性里,只是改变类下的变量l
print(p1.__dict__) --》{'name': 'alex'}
print(Chinese.l) --》['a', 'b', 'c']
静态属性@property
直接在__init__中定义公用属性,从封装性来说,它是不好的写法。
属性之访问,它亦有机制,其一便是@propery关键字。用此关键字,其获取、设置函数,须与属性名一致。
@property可以把一个实例方法变成其同名属性,以支持.号访问,它亦可标记设置限制,加以规范
1 class Room: 2 tag=1 3 def __init__(self,name,owner,width,length,heigh): 4 self.name=name 5 self.owner=owner 6 self.width=width 7 self.length=length 8 self.heigh=heigh 9 @property 10 def cal_area(self): 11 # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length)) 12 return self.width * self.length 13 def test(self): 14 print('from test',self.name) 15 r1=Room('别墅','alex',100,100,100000) 16 r2=Room('厂房','sally',1,1,1) 17 # # print('%s 住的 %s 总面积是%s' %(r1.owner,r1.name,r1.width*r1.length)) 18 # # print('%s 住的 %s 总面积是%s' %(r2.owner,r2.name,r2.width*r2.length)) 19 r1.cal_area 20 r2.cal_area 21 print(r1.cal_area) #10000 22 print(r2.cal_area) #1 23 print(r1.name) #别墅 24 print(r2.name) #厂房 25 26 # @property好处是:封装逻辑,可以自己定制逻辑 用户调用时感知不到 像在调用一个普通属性一样
类方法 @classmethod
是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。
1 class Room: 2 tag=1 3 def __init__(self,name,owner,width,length,heigh): 4 self.name=name 5 self.owner=owner 6 self.width=width 7 self.length=length 8 self.heigh=heigh 9 @property 10 def cal_area(self): 11 # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length)) 12 return self.width * self.length 13 def test(self): 14 print('from test',self.name) 15 16 @classmethod #专门供类使用的方法,类调用自己的方法 17 def tell_info(cls): 18 print(cls) 19 print('--》',cls.tag) 20 # def tell_info(self): 21 # print('---->',self.tag) 22 23 r1=Room('别墅','alex',100,100,100000) 24 Room.tell_info() --》<class '__main__.Room'> 25 --》 1
1 class people: 2 country = "china" 3 4 @classmethod 5 def getCountry(cls): 6 return cls.country 7 8 9 p = people() 10 print(p.getCountry()) # 实例对象调用类方法 china 11 print(people.getCountry()) # 类对象调用类方法 china 12 #类方法还有一个用途就是可以对类属性进行修改: 13 class people: 14 country = "china" 15 @classmethod 16 def getCountry(cls): 17 return cls.country 18 @classmethod 19 def setCountry(cls, country): 20 cls.country = country 21 22 p = people() 23 print(p.getCountry()) # 实例对象调用类方法 china 24 print(people.getCountry()) # 类对象调用类方法 china 25 26 p.setCountry("Japan") 27 print(p.getCountry()) #Japan 28 print(people.getCountry()) #Japan
静态方法@staticmethod
需要通过修饰器@staticmethod来进行修饰,静态方法不需要多定义参数
1 class Room: 2 tag=1 3 def __init__(self,name,owner,width,length,heigh): 4 self.name=name 5 self.owner=owner 6 self.width=width 7 self.length=length 8 self.heigh=heigh 9 @property 10 def cal_area(self): 11 # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length)) 12 return self.width * self.length 13 @classmethod 14 def tell_info(cls,x): 15 print(cls) 16 print('--》',cls.tag,x)#print('--》',Room.tag) 17 # def tell_info(self): 18 # print('---->',self.tag) 19 @staticmethod #类的工具包:不跟类绑定也不跟实例绑定 20 def wash_body(a,b,c): 21 print('%s %s %s正在洗澡' %(a,b,c)) 22 def test(x,y): 23 print(x,y) 24 Room.wash_body('alex','yuan','wu') #alex yuan wu正在洗澡 25 # print(Room.__dict__) 26 27 r1=Room('别墅','alex',100,100,100000) 28 # print(r1.__dict__) 29 # r1.wash_body('alex','yuanhao','wupeiqi') 30 31 Room.test(1,2) #1 2 32 r1.test(1,2) #无法调用报错
1 class people3: 2 country="china" 3 4 @staticmethod 5 def getCountry(): 6 return people3.country 7 p=people3() 8 print(p.getCountry()) #实例对象调用类方法 china 9 print(people3.getCountry()) #类对象调用类方法 china
总结:
@property跟实例绑定 (注:有self的都是跟实例绑定),既能访问类属性又能访问实例属性
@classmethod跟类绑定,能访问类属性不能访问实例属性
@staticmethod 是类的工具包,不跟类绑定也不跟实例绑定,只是名义上的归属类管理,不能使用类变量和实例变量,既不能访问类属性也不能访问实例属性
组合: 做关联 ,小的组成大的
1 class Hand: 2 pass 3 class Foot: 4 pass 5 class Trunk: 6 pass 7 class Head: 8 pass 9 10 class Person: 11 def __init__(self,id_num,name): 12 self.id_num=id_num 13 self.name=name 14 self.hand=Hand() 15 self.foot=Foot() 16 self.trunk=Trunk() 17 self.head=Head() 18 p1=Person('111111','alex') 19 print(p1.__dict__)
1 class School: 2 def __init__(self,name,addr): 3 self.name=name 4 self.addr=addr 5 6 7 def zhao_sheng(self): 8 print('%s 正在招生' %self.name) 9 10 class Course: 11 def __init__(self,name,price,period,school): 12 self.name=name 13 self.price=price 14 self.period=period 15 self.school=school 16 s1=School('oldboy','北京') 17 s2=School('oldboy','南京') 18 s3=School('oldboy','东京') 19 20 # c1=Course('linux',10,'1h','oldboy 北京') 21 # c1=Course('linux',10,'1h',s1) 22 23 msg=''' 24 1 老男孩 北京校区 25 2 老男孩 南京校区 26 3 老男孩 东京校区 27 ''' 28 while True: 29 print(msg) 30 menu={ 31 '1':s1, 32 '2':s2, 33 '3':s3 34 } 35 choice=input('选择学校>>: ') 36 school_obj=menu[choice] 37 name=input('课程名>>: ') 38 price=input('课程费用>>: ') 39 period=input('课程周期>>: ') 40 new_course=Course(name,price,period,school_obj) 41 print('课程【%s】属于【%s】学校' %(new_course.name,new_course.school.name))
1 class School: 2 def __init__(self,name,addr): 3 self.name=name 4 self.addr=addr 5 self.course_list=[] 6 def zhao_sheng(self): 7 print('%s 正在招生' %self.name) 8 class Course: 9 def __init__(self,name,price,period): 10 self.name=name 11 self.price=price 12 self.period=period 13 14 s1=School('oldboy','北京') 15 s2=School('oldboy','南京') 16 s3=School('oldboy','东京') 17 18 c1=Course('linux',10,'1h') 19 c2=Course('python',10,'1h') 20 21 s1.course_list.append(c1) 22 s1.course_list.append(c2) 23 print(s1.__dict__) 24 25 for course_obj in s1.course_list: 26 print(course_obj.name,course_obj.price) #linux 10 27 #python 10
l练习题:角色:学校、学员、课程、讲师
要求:
1. 创建北京、上海2所学校
2. 创建1inux ,python. , go 3个课程, linux\py在北京开,
3. 课程包含周期, 价格
4. 班级关联课程、讲师
5. 创建学员时,选择学校,关联班级
6. 创建讲师角色时要关联学校,
7..上.面的操作产生的数据都通过pickle序列化保存到文件里
1 import pickle 2 import hashlib 3 import time 4 def create_md5(): 5 m = hashlib.md5() 6 m.update(str(time.time()).encode('utf-8')) 7 return m.hexdigest() 8 id=create_md5() 9 time.sleep(1) 10 id1=create_md5() 11 time.sleep(1) 12 id2=create_md5() 13 print(id) 14 print(id1) 15 print(id2) 16 17 class Base: 18 def save(self): 19 with open('school.db','wb') as f: 20 pickle.dump(self,f) 21 #利用id关联学校,课程 22 class School(Base): 23 def __init__(self,name,addr): 24 self.id=create_md5() 25 self.name=name 26 self.addr=addr 27 28 class Course(Base): 29 def __init__(self,name,price,period,school): 30 self.id=create_md5() 31 self.name=name 32 self.price=price 33 self.period=period 34 self.school=school 35 36 school_obj = pickle.load(open('school.db', 'rb')) 37 print(school_obj.name,school_obj.addr) 38 s1=School('oldboy','北京') 39 s1.save()

浙公网安备 33010602011771号