python 面向对象
Posted 谢佩琪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python 面向对象相关的知识,希望对你有一定的参考价值。
1:面向对象基础
1:面向对象的基本概念
1:面向过程,(怎么做)
把完成某一个需求的 所有步骤 从头到尾 逐步实现
根据开发需求,将某些 功能独立的代码 封装 成一个函数
最后完成的代码,就顺序的调用 不同的函数
特点:
注重 步骤与过程
开发复杂的项目,没有固定的套路,开发难度很大
2:面向对象(谁来做)
相比较函数,面向对象是更大封装,根据职责 再一个对象中封装多个方法
1:要完成某一需求前,首先确定一下职责,-----要做的事情
2:根据职责 确定不同的对象,对象内部封装不同的 方法(多个)
3:最后完成代码,就是顺序的让不同的对象 调用不同的方法
特点:
1:注重 对象和职责,不同的对象 承担不同的职责
2:对应 复杂项目的开发,提供固定的套路
3:
2:类和对象的基本概念
类和对象 是面向对象的两个核心概念
1:类:
类是对一群 有相同的特征 或者 的事物的一个统称,是抽象的,不能直接使用
特征:被称为属性
行为:被称为方法
类是负责创建对象的
2:对象
对象:是由类创建出来具体存在的,可直接使用的
有哪一个类创建的对象,就拥有哪一类中定义的:
属性
方法
3:类和对象的关系
类是模板,对象是根据类这个模板创建出来的 ,现有类,再有对象
类只有一个,而对象可以多个
类中定义什么 属性和方法,对象就有什么对象和方法,不可能多,也不可能少
3:设计类的三要素和名词提炼法
1:在程序开发中,要设计一个类,满足一下三个要素
1):类名:这类事物的名字,满足大驼峰命名法
2):属性:这类事物有什么杨的特征
3):方法:这类事物有什么样的行为
大驼峰命名法:
1):每一个单词的首字母大写
2:): 单词和单词之间没有下划线
2:类名的确定
名词提炼法:分析整个业务流程,出现名词,就是要找的类
3:属性和方法的确定
对 对象的特征描述, 通常可以定义成 属性
对象具体有的行为(动词),通常可以定成方法
4:内置的dir函数查询对象的方法列表
变量,数据,函数,都会对象
在python中使用两种方法验证
1:在标识符数据 后输入一个 . 然后tab ,ipython会提示调用的方法列表
2:使用内置函数,dir 传入 标识符/数据,可以查看对象内的所有属性和方法
5:定义简单的类01_基本语法
1:python中定义只包含方法的类
class 类名: def 方法1(self,参数列表): pass def 方法2(self,参数列表): pass
第一个参数必须是self ,类名的命名规则符合大驼峰命名法
2:当一个类定义完成之后,要使用这个类创建对象
对象的变量名=类名()
6:定义简单的类02_案例演练
1:第一个面向对象的程序
需求:小猫爱吃鱼,小猫要喝水
分析:定义一个 猫类,定义两个方法eat和drink ,按需求,不需要定义属性
class Cat: def eat(self): print("小猫爱吃鱼") def drink(self,): print("小猫要喝水") #创建对象 tom = Cat() tom.eat() tom.drink()
7:定义简单的类03_接受对象变量的同时是对对象的引用
class Cat: def eat(self): print("小猫爱吃鱼") def drink(self,): print("小猫要喝水") #创建对象 tom = Cat() """ tom变量记录的是对象在内存中的地址, 也就是Tom 变量应用新建毛的对象 利用print函数输出Tom变量,是能够输出这个变量应用的对象 是有一个类创建的对象,以及内存中的地址 """ tom.eat() tom.drink() print(tom)#<__main__.Cat object at 0x0000025595BECD30>
8:定义简单的类_04 创建多个猫对象
class Cat: def eat(self): print("小猫爱吃鱼") def drink(self,): print("小猫要喝水") #创建对象 tom = Cat() """ tom变量记录的是对象在内存中的地址, 也就是Tom 变量应用新建毛的对象 利用print函数输出Tom变量,是能够输出这个变量应用的对象 是有一个类创建的对象,以及内存中的地址 """ tom.eat() tom.drink() print(tom)#<__main__.Cat object at 0x0000025595BECD30> #创建多个猫对象,他们不是一个猫对象 lazy_cat=Cat() lazy_cat.drink() lazy_cat.eat() print(lazy_cat)#<__main__.Cat object at 0x00000249FF46CE48>
9:-self-01 在类的外部给对象添加属性
1:给类的对象设置属性,非常容易,但不推荐使用
#创建对象 tom = Cat() tom.name="Tom"#创建多个猫对象,他们不是一个猫对象 lazy_cat=Cat() lazy_cat.name="大懒猫"
10:self-02 利用self在类的封装的方法中输出多个属性
class Cat: #有哪一个对象调用的方法,方法内的 self 就是哪一个对象的引用 def eat(self): print("%s爱吃鱼" % self.name) def drink(self,): print("%s要喝水"% self.name) #创建对象 tom = Cat() tom.name="Tom" tom.eat() tom.drink() #创建多个猫对象,他们不是一个猫对象 lazy_cat=Cat() lazy_cat.name="大懒猫" lazy_cat.drink() lazy_cat.eat()
11:初始化方法-01-在类的外部给对象添加属性的隐患
先调用方法,在设置属性
class Cat: def eat(self): print("%s爱吃鱼" % self.name) def drink(self,): print("%s要喝水"% self.name) #创建对象 tom = Cat() tom.eat() tom.drink() tom.name="Tom"
报错
AttributeError: ‘Cat‘ object has no attribute ‘name‘
提示:对象包含哪些属性,应该封装在类的内部
12:初始化方法-02-创建对象时自动调用初始化方法
当用类名创建对象时,会自动执行以下操作
1:为对象在内存中分配空间——创建对象
2:为对象的属性 设置初始值——初始化方法(init)
这个初始化方法就是_init_方法,是对象的内置方法,专门定义一个类 具有哪些属性方法!
class Cat: def __init__(self): print("这是一个初始化方法") tom=Cat()#使用类名创建对象的时候,会自动调用初始化方法_init_
这是一个初始化方法
13:初始化方法-03-初始化方法中定义属性
在_init_方法内部使用 self.属性名=属性的初始值 ,就可以定义属性
定义属性之后,在使用Cat创建对象,都会有该属性
class Cat: def __init__(self): print("这是一个初始化方法") self.name="tom" def eat(self): print("%s爱吃鱼" % self.name) tom=Cat() tom.eat()
print(tom.name)#打印Tom的属性
14初始化方法-04-使用参数设置属性的初始值
在开发的过程中,如果希望 创建对象的同时,就设置对象的属性,可以对init方法进行改造
1:把希望设置的属性值,定义成_init_ 方法参数
2:在方法内部使用 self.属性=形参 接受外界传递的参数
3:在创建对象时,使用 类名(属性1,属性2……)调用
class Cat: def __init__(self,new_name): print("这是一个初始化方法") #self.name="tom" self.name=new_name def eat(self): print("%s爱吃鱼" % self.name) tom=Cat("tom") tom.eat() lazy_cat=Cat("大懒猫") lazy_cat.eat()
15:-内置方法-01- del的方法和对象的生命周期
当一个对象从内存中销毁前,会自动调用_del_方法
class Cat: def __init__(self,new_name): self.name=new_name print("%s 来了"%self.name) def __del__(self): print("%s去了" % self.name) #tom是一个全局变量 tom=Cat("tom") print("-" *50)
tom 来了
--------------------------------------------------
tom去了
class Cat: def __init__(self,new_name): self.name=new_name print("%s 来了"%self.name) def __del__(self): print("%s去了" % self.name) #tom是一个全局变量 tom=Cat("tom") #del 关键字可以删除一个对象 del tom print("-" *50)
tom 来了
tom去了
--------------------------------------------------
16:内置方法-02- str- 方法定制变量的输出信息
在python中使用print输出对象变量,默认情况下,会输出这个变量 引用的对象 是 有哪一个类创建的,以及内存中的地址
在开发中,希望使用 print 输出 对象变量时,能够打印自定义的内容,就可以使用_str_内置方法了
注意:_str_方法必须返回一个字符串
class Cat: def __init__(self,new_name): self.name=new_name print("%s 来了"%self.name) def __del__(self): print("%s去了" % self.name) def __str__(self): return "我是一只小猫【%s】"%self.name #tom是一个全局变量 tom=Cat("tom") print(tom) #del 关键字可以删除一个对象 del tom print("-" *50)
tom 来了
我是一只小猫【tom】
tom去了
--------------------------------------------------
2:面向对象练习
1:小明爱跑步
1):封装
封装面向对象的一大特点,
面向对象的第一步--将属性和方法 封装到一个抽象的类中
外界使用类创建对象,然后让对象去调用方法
对象方法的细节都被封装在类的内部
2):小明爱跑步
1:小明的体重 75.0 公斤
2:小明每次跑步会减肥 0.5公斤
3:小明每次吃东西 增加 1 公斤
class Person: def __init__(self,name,weight): self.name=name self.weight=weight def __str__(self): return "我是%s,我的体重是%.2f 公斤"%(self.name,self.weight) def eat(self): print("%s是吃货,吃完东西在减肥"%self.name) self.weight+=1.0 def run(self): self.weight-=0.5 xiaoming=Person("xiaoming",75.0) xiaoming.eat() xiaoming.run() print(xiaoming)
3):提示:在对象的内部,是可以直接访问对象的属性的
同一个类中的 多个对象之间,属性互不干扰
class Person: def __init__(self,name,weight): self.name=name self.weight=weight def __str__(self): return "我是%s,我的体重是%.2f 公斤"%(self.name,self.weight) def eat(self): print("%s是吃货,吃完东西在减肥"%self.name) self.weight+=1.0 def run(self): self.weight-=0.5 xiaoming=Person("xiaoming",75.0) xiaoming.eat() xiaoming.run() print(xiaoming) xiaomei=Person("xiaomei",55.0) xiaomei.run() xiaomei.eat() print(xiaomei)
2:摆放家具
需求
1:房子(house)有户型,总面积 和家具名称列表
- 新房子没有任何的家具
2:家具(HOuseItem)有名字 和占地面积,其中
- 席梦思(bed):4平米
- 衣柜(chest):2平米
- 餐桌(table):1.5平米
4:打印房子时,要求输出:户型,总面积,剩余面积,家具名称列表
HouseItem |
naem area |
_init_(self,name,are) _str_(self)
|
House |
house_type area free_area item_list |
_init_(self,house_type,are) _str_(self) add_item(self,item) |
剩余面积
创建房子对象时,定义一个剩余面积的属性,初始值和总面积相等
当调用 add_item 方法时,向房间添加家具时 剩余面积-=家具面积
家具类
class HouseItem: def __init__(self,name,area): self.name=name self.area=area def __str__(self): return ("[%s]: %.2f 平米"%(self.name,self.area)) bed=HouseItem("席梦思",4) chest=HouseItem("衣柜",2) table=HouseItem("餐桌",1.5) print(bed) print(chest) print(table)
房子类
class House: def __init__(self,house_type,area): self.house_type=house_type self.area=area #剩余面积 self.free_area=area #家具名臣列表 self.item_list=[] def __str__(self): #python 能够自动将一对括号的代码连接在一起 return ("户型:%s 总面积:%.2f[剩余面积:%.2f] 家具:%s" %(self.house_type,self.area, self.free_area,self.item_list )) def add_item(self,item): print("要添加%s"%item) #self.item_list.append(item) my_house=House("两室一厅",60) print(my_house)
小结:让房子类调用三次 add_item,准备添加家具
添加家具
class HouseItem: def __init__(self,name,area): self.name=name self.area=area def __str__(self): return ("[%s:]%.2f 平米"%(self.name,self.area)) class House: def __init__(self,house_type,area): self.house_type=house_type self.area=area #剩余面积 self.free_area=area #家具名臣列表 self.item_list=[] def __str__(self): #python 能够自动将一对括号的代码连接在一起 return ("户型:%s 总面积:%.2f[剩余面积:%.2f] 家具:%s" %(self.house_type,self.area, self.free_area,self.item_list )) def add_item(self,item): print("要添加%s"%item) #判断家具面积 if item.area>self.free_area: print("%s面积太大了,无法添加"%(item.name)) return #将加剧名臣添加到列表 self.item_list.append(item.name) #计算剩余面积 self.free_area-=item.area bed=HouseItem("席梦思",4) chest=HouseItem("衣柜",2) table=HouseItem("餐桌",1.5) 创建房子对象 my_house=House("两室一厅",60) my_house.add_item(bed) my_house.add_item(chest) my_house.add_item(table) print(my_house)
3:封装案例
1):封装
- 封装封装面向对象的一大特点,
- 面向对象的第一步--将属性和方法 封装到一个抽象的类中
- 外界使用类创建对象,然后让对象去调用属性方法
- 对象方法的细节都被封装在类的内部
- 一个对象的 属性 是另一个类创建的 对象
01:士兵突击
需求:
1:士兵 许三多 有一把 AK47
2:士兵可以开火
3:抢能够发射子弹
4:抢装填子弹--增加子弹数量
分析:
类1: 士兵; 属性:名字,抢。 方法:开火
类2: 强; 属性:名字,子弹 方法:发射子弹
写抢类:
class Gun: def __init__(self,model): #定义抢的型号 self.model=model #抢子弹的数量 self.bullet_count = 0 def add_bullet(self,count): self.bullet_count+=count def shoot(self): pass #判断子弹是否有子弹 if self.bullet_count<=0: print("[%s] 没有子弹...."% self.model) #发射子弹 -1 self.bullet_count-=1 #提示发射 print("[%s] 突突突...[%d]"%(self.model,self.bullet_count)) ak47=Gun("ak47") ak47.add_bullet(50) ak47.shoot()
写士兵类
class Gun: def __init__(self,model): #定义抢的型号 self.model=model #抢子弹的数量 self.bullet_count = 0 def add_bullet(self,count): self.bullet_count+=count def shoot(self): pass #判断子弹是否有子弹 if self.bullet_count<=0: print("[%s] 没有子弹...."% self.model) #发射子弹 -1 self.bullet_count-=1 #提示发射 print("[%s] 突突突...[%d]"%(self.model,self.bullet_count)) class Soldier: def __init__(self,name): #姓名 self.name=name #假设每一个士兵开始都没有抢 self.gun=None def fire(self): #判断是否有抢 if self.gun is None: print("[%s] 还没有抢...."% self.name) return #高喊口号 print("冲啊...[%s]"%self.name) #让强装填子弹 self.gun.add_bullet(50) #发射子弹 self.gun.shoot() #1:创建抢的对象 ak47=Gun("ak47") #2:给抢装子弹 #ak47.add_bullet(50) #3:发射 #ak47.shoot() #1:创建士兵的对象 xusanduo=Soldier("许三多") #2:给许三多给一把枪(赋值的形式)一个对象的 属性 是另一个类创建的 对象 xusanduo.gun=ak47 #3;开枪 xusanduo.fire() #print(ak47)
身份运算符
身份运算符用于比较 内存的地址 是否一致--是否是对同一对象的引用
- 在python 中针对None 比较时,建议用 is 判断
is 与 == 区别
is 是判断两个变量 引用的对象是否是同一个
== 用于判断 应用的变量的值是否是同一个
4:私有属性和方法
1:应用场景及定义方式
实际开发中, 对象 的某些 属性或方法 可能希望 在对象的内部使用, 而不希望在外部访问到
2:定义方式
在定义属性和方法时 ,在属性或方法的名前 增加 两个下划线 定义的就是私有属性 或方法
class Women: def __init__(self,name): self.name=name self.__age=18 def __secret(self): #在对象的内部可以直接访问对象的私有属性 print("%s 的年龄是 %d"%(self.name,self.__age)) xiaomei=Women("小美") #私有属性,在外界不能直接访问 #print(xiaomei.__age) xiaomei.__secret
3:伪私有属性和方法
在前面加上 _类名
#print(xiaomei.__age) xiaomei._Women__secret
3:单继承和方法的重写
面向对象的三大特性
1:封装: 根据职责 将属性和方法 封装到一个抽象的类中
2:继承:是代码的重用
3:多态:不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
单继承
概念:子类拥有父类的所有方法和属性
语法:
class 类名(父类名): pass
- 子类继承父类,可以直接享受父类中以封装好的方法,不需要再次开发
- 子类 根据职责, 封装子类特有的 属性和方法
专业的术语:
父类===基类
子类===派生类
继承的传递性:
子类拥有 父类 以及 父类的父类 中的所有属性和方法
方法的重写
应用场景:当父类方法的实现 满足不了子类的需求,可以对方法进行 重写
重写 父类的方法 的两种情况
- 覆盖父类的方法
- 对父类方法进行扩展
1:覆盖父类的方法(父类方法的实现和子类完全不同)
class Animal: def eat(self): print("吃....") def drink(self): print("喝....") def run(self): print("跑...") class Dog(Animal): def bark(self): print("汪汪叫...") class XioaTianQuan(Dog): def fly(self): print("还会飞...") def bark(self): print("叫的跟神一样") xtq=XioaTianQuan() #如果子类中重写了父类的方法 #子类调用对象时,会调用子类中重写的方法 xtq.bark()
2:对父类方法进行扩展(父类的方法是子类的一部分)
采用 super(). 的形式,调用父类方法的执行
关于super()
- python 中super是一个特殊的类
- super()是super创建的
- 使用场景, 重写父类方法时,调用父类中封装的方法
class Animal: def eat(self): print("吃....") def drink(self): print("喝....") def run(self): print("跑...") class Dog(Animal): def bark(self): print("汪汪叫...") class XioaTianQuan(Dog): def fly(self): print("还会飞...") def bark(self): #针对子类的需求编写代码 print("叫的跟神一样") #使用super(). 调用父类中封装的方法 super().bark() #编写其他代码 xtq=XioaTianQuan() xtq.bark()
4:私有方法和属性
class A: def __init__(self): self.num1=100 self.__num2=200 def __test(self): print("私有方法 %d %d"%(self.num1,self.__num2)) class B(A): pass b=B() print(b) #在外界不能直接访问对象的私有属性调用对象的私有方法 print(b.__num2)#AttributeError: ‘B‘ object has no attribute ‘__num2‘ b._teat()
1:子类对象 不能直接 在自己的方法内部,直接访问 父类的私有属性 或私有 方法
class A: def __init__(self): self.num1=100 self.__num2=200 def test(self): print("父类的公有方法 %d"%self.__num2) class B(A): def demo(self): #在子类的对象方法中,不能直接访问父类的私有属性 print("访问父类的私有属性 %d" % self.__num2) #在子类的对象方法中,不能直接访问父类的私有方法 self.__test() b=B() print(b) #在外界不能直接访问对象的私有属性调用对象的私有方法 print(b.__num2)#AttributeError: ‘B‘ object has no attribute ‘__num2‘ b._teat()
2:子类对象 可以通过父类的 共有方法 间接访问 私有属性或私有方法
class A: def __init__(self): self.num1=100 self.__num2=200 def test(self): print("父类的公有方法 %d"%self.__num2) self.__test() def __test(self): print("私有方法 %d %d"%(self.num1,self.__num2)) class B(A): def demo(self): #在子类的对象方法中,通过直接访问父类的公有属性来访问类的私有属性 print("访问父类的私有属性 %d" % self.num1) #在子类的对象方法中,通过直接访问父类的公有方法来访问父类的私有方法 self.test() b=B() print(b) b.demo()
5: 多继承
概念
子类 可以拥有 多个父类,并且具有 所有父类的属性和方法
语法
class 子类名(父类1,父类2): pass
注意事项
- 如果父类间存在同名的属性和方法,应该尽量避免
python中的MRO,主要用于多继承时 判断方法,属性的调用路劲
print(C._mrc_)
python 3.0 中所有类的基类是object
6:多态
面向对象的三大特性
1:封装: 根据职责 将属性和方法 封装到一个抽象的类中
2:继承:是代码的重用
3:多态:不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
- 增加代码的灵活度
- 以继承和重写父类方法为前提
- 是调用方法的技巧,不会影响内部的设计
多态案列演练
需求
1:在dog类中封装game方法(普通狗简繁玩耍)
2:定义 xiaotianquan继承 dog 并重写 game 方法(哮天犬上天完)
3:定义person类, 并且封装一个和狗完的方法
- 方法内部直接让 狗对象调用 game 方法
class Dog: def __init__(self,name): self.name=name def game(self): print("%s 蹦蹦跳跳的玩耍..."%self.name) class XiaoTianQuan(Dog): def game(self): print("%s 飞上天..."%self.name) class Proson: def __init__(self,name): self.name=name def game_with_dog(self,dog): print("%s 和 %s 快乐的玩耍..."%(self.name,dog.name)) #让狗玩耍 dog.game() #创建狗对象 wangcai=Dog("旺财") #创建一个小明对象 xiaoming =Proson("小明") #让小明调用和狗完的方法 xiaoming.game_with_dog(wangcai)
案例小结:
- person类中只需让狗对象,调用game方法,而不关心具体是什么狗
- 在程序执行时,传入不同的狗对象实参,就会产生不同的结果
7:类属性,类方法,静态方法
1.1术语——实例
1:现象对象开发中,第一步,设计类
2:使用类名创建对象,创建对象的动作有两部:
- 在内存中分配对象
- 调用初始化方法 __init__为对象初始化
3:对象创建之后,内存中就有一对象实实在在的存在,-----实例
因此:通常把
- 创建出来的对象就做类的实例
- 创建对象的动作叫做实例化
- 对象的属性叫做实例属性
- 对象的方法叫做实例方法
在程序执行时:
- 对象各自拥有自己的实例属性
- 调用对象的方法,可以通过self.的方式访问自己的属性和自己的方法
结论:
- 每一个对象都有自己独立的内训空间,保存个不同的属性
- 多个对象的方法,在内存只有一份,在调用方法时需要把对象引用 传递到方法内部
1.2:类属性
1:类是一个特殊的对象
python中一切皆对象
class A: 属于类对象
a=A() 属于实例对象
-
在程序运行时,类同样会被加载到内存中
- 在python中,类是一个特殊的对象,--类对象
- 在程序运行时,类对象在内存中只有一份,使用一个类可以创建出很多的类实例
- 除了封装实例的属性和方法外,类对象还可以创建拥有自己的属性和方法
- 类属性和类方法
- 通过类名.的方式,可以方法类属性和类方法
2:概念和使用
- 类属性 就是给 类对象中 定义的 属性
- 通常会记录与这个类相关的特性
- 类属性不会记录 具体对象的特征
- 在class 关键字下方,利用赋值语句来定义
实例需求
- 定义一个工具类
- 每件工具都有自己的名字name
- 需求 --知道这个类创建了多少个工具对象
class Tools: #使用赋值语句定义类属性,记录所有工具的数量 count=0 def __init__(self,name): self.name=name #让属性值加1 Tools.count+=1 #创建工具对象 tool1=Tools("斧头") tool2=Tools("榔头") #输出工具对象的总数 print(Tools.count)
3:属性的获取机制——向上的查找机制
访问属性的两种方式
- 类名.类属性
- 对象.类属性(不推荐)
注意:
如果使用 对象.类属性=值 ,只会给对象添加一个属性,而不会影响到类属性的值
class Tools: #使用赋值语句定义类属性,记录所有工具的数量 count=0 def __init__(self,name): self.name=name #让属性值加1 Tools.count+=1 #创建工具对象 tool1=Tools("斧头") tool2=Tools("榔头") tool3=Tools("水桶") #给雷属性进行赋值 tool3.count=99 print(tool3.count) #输出工具对象的总数 print(Tools.count)
1.3:类方法
-
类方法是针对类对象定义的方法
- 在类方法内部,可以直接访问类属性,或者调用替他方法
语法如下;
@classmethod def 类方法名(cls): pass
- 通过 类名. 调用类方法调用类方法时,不需要传递cls 参数
- 在方法内部:
- 可以通过 cls. 访问类的属性
- 也可以通过 cls. 调用替他类方法
实例需求
- 定义一个工具类
- 每件工具都有自己的名字name
- 需求 --再类中封装一个 show_tools_count 的类方法,输出使用当前这个类创建的的对象个数
class Tools: #使用赋值语句定义类属性,记录所有工具的数量 count=0 def __init__(self,name): self.name=name #让属性值加1 Tools.count+=1 @classmethod def show_tool_count(cls): #在类方法的内部,可以通过cls.的方式访问类属性 print(Tools.count) #创建工具对象 tool1=Tools("斧头") tool2=Tools("榔头") tool3=Tools("水桶") #调用类方法 Tools.show_tool_count()
1.4:静态方法
开发场景:既不需要访问实例属性或调用实例方法,也不需要访问类属性或者调用类方法
@staticmethod def 静态方法名(): pass
通过 类名. 的方式来调用
class Dog: @staticmethod def run(): print("小狗会跑") Dog.run()
1.5:方法综合案例分析
需求
1:设计一个 game 类
2:属性
- 定义一个类属性top_score 记录游戏的最高分
- 定义一个实例属性 play_name 记录当前玩家姓名
3:方法:
- 静态方法show_help 显示游戏的帮助信息
- 类方法 show_top_score 显示历史最高分
- 实例方法 start_game 开始当前的玩家游戏
4:程序步骤
- 查看帮助信息
- 查看历史最高分
- 创建游戏对象
class Game: #类属性:历史最高分 top_score=0 #实例属性 def __init__(self,play_name): self.play_name=play_name @staticmethod def show_help(): print("帮助信息:让僵尸进入大门") @classmethod def show_top_score(cls): print(Game.top_score) def start_game(self): print("开始游戏了 %s"%self.play_name) #查看帮助信息 Game.show_help() #查看历史最高分 Game.show_top_score() #创建游戏对象 game=Game("小明") game.start_game()
案例小结:
1:实例方法
2:类方法
3:静态方法
8:单例模式
01:单例设计模式
目的:
- 让类创建对象,在系统中只有唯一的一个实例
- 每一次执行 类名(),返回的对象,内存地址是相同的
02:__new__方法
1:使用类名()创建对象时,python解释器 首先会 调用__new__方法,分配内存空间
2;__new__是一个object 基类提供的 内置静态方法,主要作用有两个:
- 在内存中为对象 分配空间
- 返回对象的引用
3:python解释器获得对象的引用后,将引用作为第一个参数,传递给__init__ f方法
重写 __new__ 方法非常固定
- 重写__new__方法一定要 return super().__new__(cls)
- 否则python解释器 得不到 分配空间的对象引用,就不会 调 用对象的初始化方法,就会返回一个 None
- 注意:__new__是一个静态方法,在调用时 需要传递cls参数
class MusicPlay: def __new__(cls, *args, **kwargs): #创建对象时,new方法会自动调用 print("创建对象,分配空间") #为对象分配空间 instance =super().__new__(cls) #返回对象引用 return instance play=MusicPlay() print(play)
03:python中的单例
单例:让类创建的对象,在系统中只有唯一的一个实例
- 定义一个类属性,初始值NOne,用于记录单例对象的引用
- 重写new方法
- 如果 类属性 is None ,调用父类,分配空间,,并在类属性中记录结果
- 回 类属性 中记录的对象引用
class MusicPlay: #记录一个被创建的对象引用‘ instance=None def __new__(cls, *args, **kwargs): #判断类属性属性是否是空对象 if cls.instance is None: #调用父类方法,为第一个对象分配空间 cls.instance=super().__new__(cls) #返回类属性对象保存的对象引用 return cls.instance play1=MusicPlay() print(play1) play2=MusicPlay() print(play2)
只执行一次初始化工作
使用类名()创建对象时,python解释器自动两个方法
- __new__ 分配空间
- __init__对象初始化
需求:只执行一次初始化方法
class MusicPlay: #记录一个被创建的对象引用‘ instance=None #记录是否执行初始化工作 init_flag= False def __new__(cls, *args, **kwargs): #判断类属性属性是否是空对象 if cls.instance is None: #调用父类方法,为第一个对象分配空间 cls.instance=super().__new__(cls) #返回类属性对象保存的对象引用 return cls.instance def __init__(self): #判断是否执行初始化工作 if MusicPlay.init_flag: return #如果没有执行过,在此执行初始化动作 print("初始化播放器") #修改类属性的标记 MusicPlay.init_flag=True play1=MusicPlay() print(play1) play2=MusicPlay() print(play2)
9: 异常
01:异常的概念
- 程序运行时,如果PYthon解释器 遇到一个错误,会停止程序的执行,并且提示一些错误的信息,这既是异常
- 程序停止执行,并且提示错误信息,这个动作,我们称为抛出异常
- 程序开发时,不可能将所有的特殊情况处理的面面俱到,通过异常捕获可以针对突发事件做处理,从而保证程序的稳定性和健壮性
02:捕获异常
简单的捕获语法
try: 尝试执行代码 except: 出现错误的处理
案例:要求用户输入整数
try: num=int(input("请输入一个整数:")) except: print("请输入正确的整数") print("-" *50)
03:错误类型的捕获
- 在程序执行时,可能会遇到不同类型的异常,并且需要针对不同类型的异常,做出不同的相应,就需要捕获错误类型
- 当python 解释器 抛出异常时,最后一行错误信息的第一个单词你是错误类型
案例-要求用户输入整数
需求:
1:提示用户输入一个整数
2;使用 8 除以输入的整数 并且输出
try: num=int(input("请输入一个正数:")) result=8 / num print(result) except ZeroDivisionError: print("除数不能为0") except ValueError: print("你输入的是非法的数字,请重新输入")
04:捕获位置错误
- 在程序开发时,要预测到所有可能的错误,还是有一定的难度的
- 如果希望程序无论出现任何错误,都可以抛出
try: num=int(input("请输入一个正数:")) result=8 / num print(result) except ZeroDivisionError: print("除数不能为0") except Exception as a : #把错误交给变量a,最后输出 print("未知错误 %s" % a)
05:异常捕获的完整语法
- else : 只有在没有异常的时候才执行的代码
- finally: 无论是否有异常,都会执行的代码
try: num=int(input("请输入一个正数:")) result=8 / num print(result) except ZeroDivisionError: print("除数不能为0") except Exception as a : print("未知错误 %s" % a) else: print("尝试成功") finally: print("无论是否出现错误都会执行的代码") print("-" * 50)
06:异常的传递性
- 被调用的函数会把异常传给调用方,最后会交给主控程序处理,因此,在主控程序处理异常即可;
def demo(): return int(input("请输入一个整数:")) def demo2(): return demo() #利用异常的传递性,在主程序铺货异常 try: print(demo2()) except Exception as a: print("未知错误:%s"%a)
07;抛出raise 异常
应用场景:
- 在开发中,除了代码执行出错以外,还可以根据应用程序 特有的业务需求, 主动抛出异常
示例:
- 提示用户输入密码,如果长度小于8, 抛出异常
抛出异常:
- python中提供了一个 Exception 异常类
- 在开发时,满足特定的业务需求,希望抛出异常类
- 创建Exception 的对象
- 使用raise 关键字抛出异常
def input_passwd(): #提示用户输入信息 num=input("请输入密码:") #判断密码长度是否>=8,是,返回密码 if len(num)>=8: return num #<8,主动抛出异常 print("主动抛出异常") #1)创建异常对象 ex=Exception("密码长度不够") #2)主动抛出异常 raise ex try: print(input_passwd()) except Exception as a : print(a)
10:模块和包
01:模块
1.1 模块的概念
模块是python架构的一个核心概念
- 每一个扩展名 .py 结尾的python源文件都是一个模块
- 模块名 同样是一个标识符,需要符合标识符的命名规则
- 在模块定义的全局变量 ,函数,类 都是提供给外界可用的工具
- 模块好比 工具包,想要使用这个工具包中的工具,就必须先导入这个模块
1.2:模块导入的两种方法
1)import 模块
- 在导入模块时,每个导入应该独占一行
-
import 模块1 import 模块2
通过调用就可用里面的工具
-
import xpq_01 import xpq_02 xpq_01.say_hello() xpq_02.say_hello() dog=xpq_01.Dog() print(dog) cat=xpq_02.Cat() print(cat)
- 使用as指定模块的别名
-
import xpq_01 as DogModul import xpq_02 as CatMOdul DogModul.say_hello() CatMOdul.say_hello() dog=DogModul.Dog() print(dog) cat=CatMOdul.Cat() print(cat)
2):from......import
- 从某一个工具中,导入部分工具。
-
from 模块名 import 工具名
导入之后,可以直接使用 模块的工具---全局变量,函数,类
- 注意:如果两个模块,存才同名的函数,那么 后倒入的模块,会覆盖先导入的模块
- 开发时,import 代码 应该统一写在代码的顶部,一旦发现冲突,可以使用as 关键字 给其中的一个工具起一个别名
1.3 模块的搜索顺序
- 搜索当前目录 模块名文件,如果有,就直接导入,如果没有,就搜索系统目录
- 模块有一个内置的属性,__file__可以查看模块的完整路径
-
import xpq_01 as DogModul import xpq_02 as CatMOdul print(DogModul.__file__)#D:PycharmProjects模块xpq_01.py DogModul.say_hello() CatMOdul.say_hello() dog=DogModul.Dog() print(dog) cat=CatMOdul.Cat() print(cat)
1.4:原则--每一个文件都应是可以被导入的
- 导入文件时,文件中没有任何缩进的代码,都会被执行一遍
实际开发场景
- 开发人员通常会在 模块下方 增加一些测试代码,仅在模块内使用,仅在模块内使用,而在其他文件中不需要执行
__name__属性
- __name__属性可以做到, 测试模块的代码 只在测试的模块中运行,而在被导入时不会被执行
- __name__是python中的一个内置属性,记录这一个字符串,
- 如果是被其他文件导入的,__name__就是模块名
- 如果是当前执行的程序 __name__是__main__
__name__模块
def say_hello(): print("你好你好,我是 say_hello") #如果直接执行模块,__main__ if __name__=="__main__": print (__name__) #文件被导入时,能够被执行的代码不被执行 print ("小明开发的模块") say_hello()
__name__导入模块
import __name__模块 print ("-" *50)
很多python文件中看到以下格式代码
#导入模块 #定义变量‘’ #定义类 #定义函数 #在代码的最下方 def main() #。。。 pass #根据__name__判断,是否执行下方代码 if __name__=="__main__": main()
02:包
概念:
- 包是一个包含多个模块的 特殊目录
- 目录下有一个特殊文件__init__
- 包名的命名方式和变量一致
好处
- 使用import 包名 可以一次性的导入包中的所有模块
- 哟使用 包 中的模块,需要 __init__.py 中指定 对外界提供中的模块列表
#从当权目录 导入 模块列表 from . import send_message from . import receive_message
11:文件操作
12:文本编码
13:内建函数eval
14:反射
# 反射 : 是用字符串类型的名字 去操作 变量 #反射是对象中的属性和方法 # hasattr getattr setattr delattr class A: def func(self): print(‘in func‘) a=A() a.name=‘alex‘ a.age=63 #反射的对象属性 ret=getattr(a,‘name‘)#a 对象名, ‘name’属性名 print(ret )#alex #反射方法属性 a.func() ret = getattr(a,‘func‘) ret()
反射类的属性和方法
class A: price = 20 @classmethod def func(cls): print(‘in func‘) # 反射类的属性 A.price print(getattr(A,‘price‘))#20 # # 反射类的方法 :classmethod staticmethod A.func() if hasattr(A,‘func‘): getattr(A,‘func‘)#in func
模块中的属性和方法
#模块 #import my # 反射模块的属性 #print(my.day) #print(getattr(my,‘day‘)) # 反射模块的方法 #getattr(my,‘wahaha‘)() # 内置模块也能用 # time # asctime import time print(getattr(time,‘time‘)()) print(getattr(time,‘asctime‘)())
import sys print(sys.modules[‘__main__‘]) # # 反射自己模块中的变量 #print(getattr(sys.modules[‘__main__‘],‘year‘)) # # # 反射自己模块中的函数 getattr(sys.modules[‘__main__‘],‘qqxing‘)() 变量名 = input(‘>>>‘) print(getattr(sys.modules[__name__],变量名)) # 要反射的函数有参数怎么办? # print(time.strftime(‘%Y-%m-%d %H:%M:S‘)) # print(getattr(time,‘strftime‘)(‘%Y-%m-%d %H:%M:S‘)) # 一个模块中的类能不能反射得到 # import my # print(getattr(my,‘C‘)()) # if hasattr(my,‘name‘): # getattr(my,‘name‘)
以上是关于python 面向对象的主要内容,如果未能解决你的问题,请参考以下文章