面向对象程序设计
一、类和对象
类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,类就是一系列对象相似特征和技能的结合体,针对同样的事物,站在不同的角度得到的类是不同的。需要注意的是:
- 站的角度不同,定义出的类是截然不同的;
- 现实中的类并不完全等于程序中的类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类等;
- 有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类。
1、对象和类的先后关系
在现实世界中一定是先有对象后有类;但在程序中,却是恰恰相反,一定是先定义类,后调用类来产生对象。
# 先定义类 class LuffyStudent: school = 'luffycity' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') # 后产生对象 stu1 = LuffyStudent() # 不是执行类体,而是实例化 stu2 = LuffyStudent() stu3 = LuffyStudent() print(stu1) # <__main__.LuffyStudent object at 0x10401ae80>
注意:
- 类中可以有任意Python代码,这些代码的类定义阶段便会执行,因而会产生新的名称空间,用来存放类的变量名和函数名,可以通过__dict__查看。
- 类中定义的名字,都是类的属性,点是访问属性的语法。
- 对于经典类来说我们可以通过该字典操作类名称空间的名字,但新式类有限制。
2、__dict__方法
__dict__是一个字典,键为属性名,值为属性值。类的__dict__存储所有实例共享的变量和函数(类属性,方法等),类的__dict__并不包含其父类的属性。
class LuffyStudent: school = 'luffycity' def learn(self): print('is learning') def eat(self): print('is sleeping') print(LuffyStudent.__dict__) print(LuffyStudent.__dict__['school']) print(LuffyStudent.__dict__['learn']) """ {'__module__': '__main__', 'school': 'luffycity', 'learn': <function LuffyStudent.learn at 0x101eb3620>, 'eat': <function LuffyStudent.eat at 0x101f211e0>, '__dict__': <attribute '__dict__' of 'LuffyStudent' objects>, '__weakref__': <attribute '__weakref__' of 'LuffyStudent' objects>, '__doc__': None} luffycity <function LuffyStudent.learn at 0x101eb3620> """
需要注意__dict__与dir()函数的区别:
1、dir()是Python提供的一个API函数,dir()函数会自动寻找一个对象的所有属性(包括从父类继承的属性),包括__dict__中的属性。
2、__dict__是dir()的子集,一个实例的__dict__属性仅仅是那个实例的实例属性的集合,并不包含该实例的所有有效属性。
3、特殊类属性
__name__:查看类的名字(这里的类名是字符串类型的)
__dict__:查出一个字典,key为属性名,value为属性值
__doc__:显示注释
dir(类名):查看类,以列表的形式显示出来
__module__:类定义所在的模块
__class__:
isinstance(stu1, LuffyStudent):判断对象是否是类的实例
4、引用类的属性
在使用类时,可以对类的属性进行增删改查操作。
class LuffyStudent: school = 'luffycity' def learn(self): print('is learning') def eat(self): print('is sleeping') # 查看 print(LuffyStudent.school) # luffystudent.__dict__['school'] print(LuffyStudent.learn) # Luffystudent.__dict_ ('learn') """ luffycity <function LuffyStudent.learn at 0x101cb3620> """ # 增 LuffyStudent.country = 'China' print(LuffyStudent.__dict__) print(LuffyStudent.country) """ {'__module__': '__main__', 'school': 'luffycity', 'learn': <function LuffyStudent.learn at 0x101eb3620>, 'eat': <function LuffyStudent.eat at 0x1040211e0>, '__dict__': <attribute '__dict__' of 'LuffyStudent' objects>, '__weakref__': <attribute '__weakref__' of 'LuffyStudent' objects>, '__doc__': None, 'country': 'China'} China """ # 删 del LuffyStudent.country # 改 LuffyStudent.school = 'whu' print(LuffyStudent.school) """ whu """
5、实例化
调用类(实例化),得到程序中的对象。
class LuffyStudent: school = 'luffycity' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') # 后产生对象 stu1 = LuffyStudent() # 不是执行类体,而是实例化 stu2 = LuffyStudent() stu3 = LuffyStudent() print(stu1) print(stu2) print(stu3) """ <__main__.LuffyStudent object at 0x10401ae80> <__main__.LuffyStudent object at 0x10401af28> <__main__.LuffyStudent object at 0x10401af60> """
实例化完成后,stu1、stu2、stu3都一样了,这三者除了相似的属性外还有各种不同的属性,就需要用到__init__方法了。
6、__init__方法:为对象定制自己独有的特征
__init__方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值。
实例化后会产生对象的名称空间,对象的名称空间也可以用stu1.__dict__查看:{'Name': '百合', 'Sex': '女', 'Age': 12}
class LuffyStudent: school = 'luffycity' def __init__(self, name, sex, age): # 实例化时自动调用 self.Name = name self.Sex = sex self.Age = age def learn(self): print('is learning') def eat(self): print('is sleeping') # 产生对象 stu1 = LuffyStudent('百合', '女', 12) print(stu1.Name) """ 加上__init__方法后,实例化的步骤 1、先产生一个空对象 2、LuffyStudent.__init__(stu1, '百合', '女', 12) """ # 查 print(stu1.__dict__) print(stu1.Name) print(stu1.Sex) print(stu1.Age) """ {'Name': '百合', 'Sex': '女', 'Age': 12} 百合 女 12 """ # 改 stu1.Name = '李二丫' print(stu1.__dict__) print(stu1.Name) """ {'Name': '李二丫', 'Sex': '女', 'Age': 12} 李二丫 """ # 删除 del stu1.Name print(stu1.__dict__) """ {'Sex': '女', 'Age': 12} """ # 增 stu1.class_name = 'python开发' print(stu1.__dict__) """ {'Sex': '女', 'Age': 12, 'class_name': 'python开发'} """ stu2 = LuffyStudent('李三炮', '男', 22) print(stu2.__dict__) """ {'Name': '李三炮', 'Sex': '男', 'Age': 22} """
当类有了__init__方法后,实例化时先调用类产生空对象,再调用__init__()方法。
上述代码中也包含有对象的使用,对对象的增删改查操作。
7、关于self
self,就是实例本身!你实例化时python解释器会自动把这个实例本身通过self参数传进去。
self是实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给它起个别的名字,但是一般正常人都不会这么做,因为改了别人就不认识了。
注意:def __init__(self): 这句话可以写也可以不写,只要有参数参进来的时候就必须得写
def 方法名(self):这里的self必须得写
二、属性查找
类中有两种属性:数据属性和函数属性。
class LuffyStudent: school = 'luffycity' def __init__(self, name, sex, age): # 实例化时自动调用 self.Name = name self.Sex = sex self.Age = age def learn(self): print('%s is learning' % self.Name) def eat(self): print('%s is sleeping' % self.Name) # 产生对象 stu1 = LuffyStudent('百合', '女', 12) stu2 = LuffyStudent('李三炮', '男', 38) stu3 = LuffyStudent('张铁蛋', '男', 48) # 对象:特征和技能的结合体 # 类:类是一系列对象相似的特征与相似技能的结合体
1、类的数据属性是所有对象共享的
# 类中数据属性:是所有对象共有的(都是一样的) print(LuffyStudent.school, id(LuffyStudent.school)) print(stu1.school, id(stu1.school)) print(stu2.school, id(stu2.school)) """内存地址一样 luffycity 4325120112 luffycity 4325120112 luffycity 4325120112 """
2、类的函数属性是绑定给对象用的,称为绑定到对象的方法。
# 类中函数属性:是绑定给对象的,绑定到不同的对象是不同的绑定方法,对象调用绑定方法时,会把对象本身当做第一个参数传入,传给self print(LuffyStudent.learn) LuffyStudent.learn(stu1) """ <function LuffyStudent.learn at 0x1040211e0> 百合 is learning """ print(stu1.learn) print(stu2.learn) print(stu3.learn) """绑定方法,每个人的函数内存地址不同 <bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x10402cc18>> <bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x10402cc50>> <bound method LuffyStudent.learn of <__main__.LuffyStudent object at 0x10402cc88>> """
3、属性查找顺序
优先在对象中查找,对象中没有再在类中查找,类中没有会去父类查找,最后都没有则抛出异常。
# 属性查找,优先对象中查找,对象中没有在类中查找 # stu1.x = 'from stu1' LuffyStudent.x = 'from Luffycity class' print(stu1.__dict__) print(stu1.x) """ {'Name': '百合', 'Sex': '女', 'Age': 12} from Luffycity class """
三、绑定方法
在属性查找那一小节中,定义了类并实例化出了三个对象stu1、stu2、stu3。
class LuffyStudent: school = 'luffycity' def __init__(self, name, sex, age): # 实例化时自动调用 self.Name = name self.Sex = sex self.Age = age def learn(self): print('%s is learning' % self.Name) def eat(self): print('%s is sleeping' % self.Name) # 产生对象 stu1 = LuffyStudent('百合', '女', 12) stu2 = LuffyStudent('李三炮', '男', 38) stu3 = LuffyStudent('张铁蛋', '男', 48)
类中定义的函数(没有被任何装饰器装饰的)是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数。
LuffyStudent.learn(stu1) LuffyStudent.learn(stu2) LuffyStudent.learn(stu3) """ 百合 is learning 李三炮 is learning 张铁蛋 is learning """
类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法
强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将‘谁’本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)。
stu1.learn() stu2.learn() stu3.learn() """ 百合 is learning 李三炮 is learning 张铁蛋 is learning """
注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。
四、类即类型
python中一切皆为对象,且Python3中类与类型是一个概念,类型就是类。
class student: school = 'whu' # python当中一切皆对象,在python3中统一了类和类型的概念 print(list) print(dict)∂ print(student) """ <class 'list'> <class 'dict'> <class '__main__.student'> """
以list类型举例,类型list就是类list,对类实例化后,对象调用绑定方法append为对象添加元素。
>>> list <class 'list'> #实例化的到3个对象l1,l2,l3 >>> l1=list() >>> l2=list() >>> l3=list() #三个对象都有绑定方法append,是相同的功能,但内存地址不同 >>> l1.append <built-in method append of list object at 0x10b482b48> >>> l2.append <built-in method append of list object at 0x10b482b88> >>> l3.append <built-in method append of list object at 0x10b482bc8> #操作绑定方法l1.append(3),就是在往l1添加3,绝对不会将3添加到l2或l3 >>> l1.append(3) >>> l1 [3] >>> l2 [] >>> l3 [] #调用类list.append(l3,111)等同于l3.append(111) >>> list.append(l3,111) #l3.append(111) >>> l3 [111]
五、面向对象小结
1、定义及调用的固定格式
class 类名: def __init__(self,参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法名(self):pass def 方法名2(self):pass 对象名 = 类名(1,2) #对象就是实例,代表一个具体的东西 #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法 #括号里传参数,参数不需要传self,其他与init中的形参一一对应 #结果返回一个对象 对象名.对象的属性1 #查看对象的属性,直接用 对象名.属性名 即可 对象名.方法名() #调用类中的方法,直接用 对象名.方法名() 即可
2、运用面向对象好处
(1)将数据和专门操作该数据的功能整合到一起
避免定义一大堆全局变量。
class MySQLHandler: def __init__(self,host,port,db,charset='utf8'): self.host=host self.port=port self.db=db self.charset=charset self.conn=connect(self.host,self.port,self.db,self.charset) def exc1(self,sql): return self.conn.execute(sql) def exc2(self,sql): return self.conn.call_proc(sql) obj=MySQLHandler('127.0.0.1',3306,'db1') obj.exc1('select * from tb1;') obj.exc2('存储过程的名字')
(2)可扩展性高
定义类并产生多个对象
class Chinese: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex p1=Chinese('egon',18,'male') p2=Chinese('alex',38,'female') p3=Chinese('wpq',48,'female')
如果新增一个类属性,会立刻反映给所有对象,而对象却无需修改。
class Chinese: country='China' def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def tell_info(self): info=''' 国籍:%s 姓名:%s 年龄:%s 性别:%s ''' %(self.country,self.name,self.age,self.sex) print(info) p1=Chinese('egon',18,'male') p2=Chinese('alex',38,'female') p3=Chinese('wpq',48,'female') print(p1.country) p1.tell_info()
3、面向对象练习题
练习1:编写一个学生类,产生一堆学生对象。
要求:有一个计数器(属性),统计总共实例了多少个对象
class Student: # 类名头字母大写 school = 'whu' count = 0 def __init__(self, name, age, sex): # 为对象定制对象自己独有的特征 self.name = name self.age = age self.sex = sex # self.count += 1 # 每个对象都是1,无法实现累加,student类的count一直都是0 Student.count += 1 def learn(self): print('%s is learning' % self.name) def eat(self): print('%s is eating very happy!' % self.name) stu1 = Student('alex', 'male', 38) # 实例化一次就触发一次__init__ stu2 = Student('jinxing', 'female', 48) stu3 = Student('egon', 'male', 18) print(Student.count) print(stu1.count) print(stu2.count) print(stu3.count) print(stu1.__dict__) print(stu3.__dict__) """ 3 3 3 3 {'name': 'alex', 'age': 'male', 'sex': 38} {'name': 'egon', 'age': 'male', 'sex': 18} """
练习2:模仿王者荣耀定义两个英雄类, (10分钟)
要求:1、英雄需要有昵称、攻击力、生命值等属性;
2、实例化出两个英雄对象;
3、英雄之间可以互殴,被殴打的一方掉血,血量小于0则判定为死亡。
class Garen: camp = 'Demacia' def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity class Riven: camp = 'Noxus' def __init__(self, nickname, life_value, aggresivity): self.nickname = nickname self.life_value = life_value self.aggresivity = aggresivity def attack(self, enemy): enemy.life_value -= self.aggresivity g1 = Garen('草丛伦', 100, 30) r1 = Riven('可爱的瑞雯', 80, 50) print(r1.life_value) g1.attack(r1) # 攻击了一刀 r1.attack(g1) print(r1.life_value) print(g1.life_value)
4、判断是函数还是方法?
函数:函数是封装了一些独立的功能,可以直接调用,python内置了许多函数,同时可以自建函数来使用。
方法:方法和函数类似,同样封装了独立的功能,但是方法是需要通过对象来调用的,表示针对这个对象要做的操作,使用时采用点方法。