OPP介绍
programming的核心原则需要铭记:
1. 写重复代码是非常不好的低级行为
2. 写的代码需要经常变更
初学者虽然理解面对对象编程(定义类,方法,三大特性等),但是一正真写程序,就喜欢用面对过程式编程来写代码(一个个函数)。 这主要是没有充分理解OPP所带来的好处。 开发正规的程序和写只运行一次的小脚本最大不同是,代码总需要不断的更改,不管是修改bug或者添加新功能等,所以为了日后方便程序的修改和扩张,写的代码一定要可读性好、易扩展。 OPP比函数编程更加易读,易改。 以下简单的仿cs游戏实例就可以看出这点。
为每个角色开发以下几个功能被打中后就会掉血的功能: 开枪功能 换子弹 买枪 跑、走、跳、下蹲等动作 保护人质(仅适用于警察) 不能杀同伴 。。。
''' 创建4个角色 ''' roles = { 1: {'name': 'Alex', 'role': 'terrorist', 'weapon': 'AK47', 'life_value': 100, 'money': 15000, }, 2: {'name': 'Jack', 'role': 'police', 'weapon': 'B22', 'life_value': 100, 'money': 15000, }, 3: {'name': 'Rain', 'role': 'terrorist', 'weapon': 'C33', 'life_value': 100, 'money': 15000, }, 4: {'name': 'Eirc', 'role': 'police', 'weapon': 'B51', 'life_value': 100, 'money': 15000, }, } # 调用角色的方式 print(roles[1]) # Alex print(roles[2]) # Jack def shot(by_who): # 开枪功能,开了抢后减少子弹 pass def got_shot(who): # 中枪后要减血 who['life_value'] -= 10 pass def buy_gun(who, gun_name): # 检查钱够否,买了抢扣钱 pass
但是,从以上例子就可以看出,过程式编程的缺陷问题:
1. 每个角色下设的属性种类(包括name, role, weapon, life_value, money)都一样。创建新角色时,如果属性名字错误(weapon -> wepon),就无法读取。而且复制黏贴创建也不是很高级。同时,当要增加属性类别时,得一个个加,极其崩溃。
2. terrorist和policy 有相同的功能,也有不同的功能。 以上做法无法区分。 角色无法个性化
3. 减血应该只能通过got_shot()函数。但是以上程序,可以直接通过调用 roles[who]['life_value'] 绕过got_shot直接减血,等于是个作弊器。
用OPP的话,大量缩减代码量,同时更易读。 见下:
class Role(object): # 定义一个类,class是定义类的语法, role是类名,(object)是新式类的写法,必须这样写 def __init__(self,name,role, weapon, life_value=100, money=15000): # 初始化函数,在生成一个角色时要初始化的一些属性 self.name = name self.role = role self.weapon = weapon self.life_value = life_value self.money = money def shot(self): print("shooting") def got_shot(self): print("ah..., i got shot...") def buy_gun(self, gun_name): print("just bought %s" %gun_name) r1 = Role("alex", "police", "ak47") # 生成一个角色,会自动把参数传给role下面的__init__(...)方法 r2 = Role("Jack", 'terrorist', 'b22') # 生成一个角色
何时适用OPP 面向对象编程呢?
- 应用场景: SSH 链接操作
- 模版
- 很多函数都需要传公共的参数
基本定义及语法
OPP编程是利用“类”和“对象”来创建各种模型来实现对最后的需求。 其定义如下:
class 类
类似于模版。 一个类即是对一类拥有相同属性的对象的抽象、蓝图、及原型。在类中,定义了这些object都具有的属性(variables), 共同的方法或者功能。
object 对象
对象即是一个类的实例化后的实例(通过模版刻画出来的实物)。 一个类必须经过实例化后方可在程序中调用, 一个类可以实例化多个对象。 每个对象亦可有不同的特性。
实例化其实就是以类为模版,在内存中开辟一块空间,存上数据,赋值成一个变量名。 其他关于类和对象,见以下示例:
class Dog(object): print("hello,I am a dog!") d = Dog() #实例化这个类, #此时的d就是类Dog的实例化对象 #实例化,其实就是以Dog类为模版,在内存里开辟一块空间,存上数据,赋值成一个变量名
class Dog(object): def __init__(self,name,dog_type): #成员属性/构造函数/构造方法 == 初始化方法 self.name = name self.type = dog_type def sayhi(self): # 类的方法 print("hello,I am a dog, my name is ",self.name) d = Dog('LiChuang',"京巴") # Dog(d, “litchang” ) # 实力化后产生的对象 d.sayhi()
总结下,类、实例化对象 的内存储存方式见下图:
类的语法
过程: 定义类 -> 实例化 -> 实例对象
类的语法涉及:
1. 构造函数 或 初始化函数, 即指类实例化后的对象初始化的一些属性
表达式: def __initial__(self)
2. 成员变量 或 属性 或 字段,即在__initial__(self)函数下设置的内容
表达式: self.name = name 或 self.life_value = life_value 等
其中,self指的是实例化后的对象本身。 之后将这个对象作为参数传入类的功能。 见下图
3. 方法 或 动态属性
表达式: def sayhi(self)
4. 私有属性,定义在构建函数之下,前缀 __ 。 一般,对外不可访问,但是可以通过内部方法调用访问。
表达式: self.__heart = "normal"
私用属性的访问方式有三种,案例见下代码:
- 通过类内的方法下设访问
- 通过类内的对外提供只读的访问借口 return self.__heart
- 强行访问 r1._Role__heart()
5. 公有属性, 即所有属于类的对象都可以访问的属性。成员属性不是公有属性。 公有属性直接在class下面,不在任何built-in 函数里面。
6. 析构方法, 实例销毁的时候自动调用。 变量实例化后,在内存中就存了数据,不会自动清除。 del 用来销毁实例, 自动调用 __del__(self)。 销毁实例,只是撤销r2这个名字(类似门牌号)和内存地址之间的引用关系,并没有删除内存地址里的内容。 python有自动垃圾清除机制,如果发现门牌号摘掉了,就删除内存里的数据。 主要用于一些程序的收尾工作
一般搜索过程,是先搜索成员属性,在到父集找公有属性。同时,除了公有属性是共享的,下设函数也是公有,也是共享的。 所有在函数需要导入参数self。
以下例子中展示了1-5的使用方法
class Role(object): # 定义一个类,class是定义类的语法, role是类名,(object)是新式类的写法,必须这样写 nationality = "JP" # 5. 公有属性, 在类里直接定义的属性即是公有属性 def __init__(self,name,role, weapon, life_value=100, money=15000): # 1. 构造函数或初始化函数,在生成一个角色时要初始化的一些属性 self.name = name # 2. 成员变量,属性 self.role = role self.weapon = weapon self.life_value = life_value self.money = money self.__heart = 'normal' # 4. 私有属性,私有属性对外部不可访问;但内部客访问见下 def shot(self): # 3. 方法/功能,动态属性 print("shooting") self.__heart = 'DIE' print(self.__heart) def got_shot(self): print("ah..., i got shot...") self.__heart = "die" print(self.__heart) # 4. 私有属性的内部访问,比较推荐的方式,方法一 def buy_gun(self, gun_name): print("just bought %s" %gun_name) def get_heart(self): return self.__heart # 4. 私有属性, 对外提供只读访问借口 方法二 def __del__(self): # 6. 析构方法,实例销毁是自动调用 print("del...run...") r1 = Role("alex", "police", "ak47") # 生成一个角色,会自动把参数传给role下面的__init__(...)方法 r2 = Role("Jack", 'police dog', 'b13') # 生成一个角色 r1.got_shot() print(r1.get_heart()) print(r1._Role__heart) # 4. 私有属性的强制访问 方法三 Role.nationality = "usa" # 5. 更改类的公有属性 print(r1.nationality) # 5. 打印公有属性 r1.nationality = "tailand" # 5. 从新在r1对象下新建了一个nationality 的成员变量,不是公有变量 print(r1.nationality) del r2 # 6. 销毁实例, 自动调用 __del__(self)。 销毁实例,只是撤销r2这个名字(类似门牌号)和内存地址之间的引用关系,并没有删除内存地址里的内容。 python有自动垃圾清除机制,如果发现门牌号摘掉了,就删除内存里的数据
Reference:
day 6 by Alex 老男孩课件