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()      属于实例对象

  1. 在程序运行时,类同样会被加载到内存中

  2. 在python中,类是一个特殊的对象,--类对象
  3. 在程序运行时,类对象在内存中只有一份,使用一个类可以创建出很多的类实例
  4. 除了封装实例的属性和方法外,类对象还可以创建拥有自己的属性和方法
  5. 类属性和类方法
  6. 通过类名.的方式,可以方法类属性和类方法

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:属性的获取机制——向上的查找机制

访问属性的两种方式

  1. 类名.类属性
  2. 对象.类属性(不推荐)

注意:

如果使用 对象.类属性=值   ,只会给对象添加一个属性,而不会影响到类属性的值

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中的单例

单例:让类创建的对象,在系统中只有唯一的一个实例

  1. 定义一个类属性,初始值NOne,用于记录单例对象的引用
  2. 重写new方法
  3. 如果 类属性 is  None  ,调用父类,分配空间,,并在类属性中记录结果
  4. 回 类属性 中记录的对象引用
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  模块的搜索顺序

  1. 搜索当前目录 模块名文件,如果有,就直接导入,如果没有,就搜索系统目录
  2. 模块有一个内置的属性,__file__可以查看模块的完整路径
  3. 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 面向对象的主要内容,如果未能解决你的问题,请参考以下文章

python之路之前没搞明白4面向对象(封装)

Python面向对象学习之八,装饰器

python:第二部分:面向对象:面向对象object orinted

面向面试编程代码片段之GC

Python 面向对象

面向对象编程其实很简单——Python 面向对象(初级篇)