一文彻底搞懂python面向对象编程(全网最全!!!)

  • Post author:
  • Post category:python

面向对象:

独立独立存在,在程序开发时,现有类再有对象(属性 方法)

类名 大驼峰命名法

属性 这类事物具有什么样的特征

方法 这类事物具有什么样的行为

对对象的特征描述,通常可以定义成属性

对象具有行为,通常可以定义成方法

面向过程的基础语法 dir内置函数 ,使用dir传入标识符/数据 可以查看对象内的所有属性及方法。

提示 __方法名__ 格式的方法是python提供的内置方法/属性

方法名 类型 作用
__new__ 方法 创建对象时,会被自动调用
__init__ 方法 对象被初始化时,会被自动调用
__del__ 方法 对象被从内存中销毁前,会被自动调用
__str__ 方法 返回对象的描述信息,print函数输出使用

定义一个简单的类

面向对象是一个更大的封装,一个类中封装多个方法,这样通过这个类创建的对象,就可以直接调用这些方法了。

class 类名:
	def 方法1(self,参数列表)pass
	def 方法2(self,参数列表)pass

方法的定义格式和之前学习的函数几乎一样,区别就是第一个参数必须为self 类名的命名规则要符合大驼峰命名法!

创建对象 对象变量=类名()

面向对象的第一个对象程序:

需求 : 小猫爱吃鱼 小猫要喝水

class Cat:
    """这是一个猫类"""
    def eat(self):
        print("小猫爱吃鱼")
    def drink(self):
        print("小猫要喝水")
#创建对象
tom = Cat()
tom.eat()
tom.drink()

引用的概念:与面向过程一样

%x 打印出的是16进制,%d是十进制打印数字

在cat类中创建多个对象

class Cat:
    def eat(self):
        print("小猫爱吃鱼")
    def drink(self):
        print("小猫要喝水")
#创建对象
tom = Cat()
tom.eat()
tom.drink()
print(tom)
#再创建一个对象
lazy_cat =Cat()
lazy_cat.eat()
lazy_cat.drink()
print(lazy_cat)
#lazy_cat2 =lazy_cat是一个对象
#可以使用相同的类创建不同的对象
lazy_cat2=lazy_cat
print(lazy_cat2)

案例改造 给对象增加属性

在python中,要给对象设置属性非常的容易,但是不推荐使用,因为对象属性的封装应该封装在类的内部 只需要在类的外部的代码中直接通过 .来设置一个属性即可。

class Cat:
    def eat(self):
        print("小猫爱吃鱼")
    def drink(self):
        print("小猫要喝水")
#创建对象
tom = Cat()
#可以使用.属性名 利用赋值语句就可以了 在pycharm的单步调试可以看到name的属性
#不推荐使用 因为只在外面强制修改了名字
tom.name ="Tom"
tom.eat()
tom.drink()

哪一个对象调用方法,方法内的self就是哪一个对象的引用

在类封装的方法内部,self就表示当调用方法的对象自己

在调用方法时,开发用户不需要传递self参数。

在方法内部可以通过self. 访问对象属性

也可以通过self. 调用其他的对象方法

class Cat:
    def eat(self):
        #哪一个对象调用的方法,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.eat()
lazy_cat.drink()

在类的外部调用如果赋值在调用后面,否则就会报错。所以,非常不推荐在类外面进行创建。

对象的初始化方法:

当使用类名创建对象时,会自动执行以下操作

为对象分配空间 为对象的属性设置初始值,初始化方法(init)

这个初始化方法就是 __init__方法 __init__是对象的内置方法

__init__方法是专门用来定义一个类具有哪些属性的方法

Cat中增加 __init__方法,验证该方法在创建对象时会被自动调用。

class Cat:
    def __init__(self):
        print("这是一个初始化方法")
#使用类名()创建对象的时候,会自动调用初始化方法__init__
tom =Cat()

在初始化方法内部定义属性

__init__方法内部使用 self.属性名 =属性的初始值就可以定义属性

在定义属性之后,再使用Cat类创建对象,都会拥有该属性。

class Cat:
    def __init__(self):
        print("这是一个初始化方法")
        #self.属性名 =属性的初始值
        self.name="Tom"
#使用类名()创建对象的时候,会自动调用初始化方法__init__
tom =Cat()
print(tom.name)

改造初始化方法–初始化的同时设置初始值

在开发过程中,如果希望在创建对象的同时,就设置对象的属性,就可以对 __init__方法进行改造,把希望设置的属性值,定义成 __init__方法的参数 在方法内部使用 self.属性 =形参 接收外部传递的参数。 在创建对象时,使用 类名(属性1,属性2,……)

class Cat:
    def __init__(self,new_name):
        print("这是一个初始化方法")
        #self.属性名 =属性的初始值
        self.name =new_name
    def eat(self):
        print("%s 爱吃鱼"%self.name)
#使用类名()创建对象的时候,会自动调用初始化方法__init__
tom =Cat("大懒猫")
tom.eat()
print(tom.name)

__del__方法:

当一个对象被从内存中销毁前,再自动调用 __del__方法

应用场景:

__init__方法改造初始化方法,可以让创建对象更加灵活

__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(tom.name)
#del关键字可以删除一个对象
del tom
print("=="*50)

__str__方法:

如果再开发中使用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)

面向对象的封装案例:

将属性和方法封装到一个抽象的类中 对象方法的细节都被封装在类的内部。

小明爱跑步

1.小明体重75.0公斤

2.小明每次跑步会减肥0.5公斤

3.小明每次吃东西会增加1公斤

class Person:
    def __init__(self,name,weight):
        #self.属性 =形参
        self.name =name
        self.weight =weight
    def __str__(self):
        return "我的名字叫%s 体重是%.2f"%(self.name,self.weight)
    def run(self):
        print("%s爱跑步,跑步锻炼身体"%(self.name))
        self.weight =self.weight-0.5
    def eat(self):
        print("%s是吃货,吃完这顿再减肥"%(self.name))
        self.weight= self.weight+1
xiaoming =Person("小明",75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)

小明爱跑步拓展–小美也爱跑步

1.小明和小美都爱跑步

2.小明体重 75.0公斤

3.小美体重 45.0公斤

4.每次跑步都会减少0.5公斤

5.每次吃东西都会增加1公斤

class Person:
    def __init__(self,name,weight):
        #self.属性 =形参
        self.name =name
        self.weight =weight
    def __str__(self):
        return "我的名字叫%s 体重是%.2f"%(self.name,self.weight)
    def run(self):
        print("%s爱跑步,跑步锻炼身体"%(self.name))
        self.weight =self.weight-0.5

    def eat(self):
        print("%s是吃货,吃完这顿再减肥"%(self.name))
        self.weight= self.weight+1
xiaoming =Person("小明",75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)
xiaomei =Person("小美",45)
xiaomei.eat()
xiaomei.run()
print(xiaomei)

在同一个类创建的多个类,属性是互不干扰的!!!!

案例:(摆放家具)

需求

1.房子有户型,总面积和家具名称列表 新房子没有任何的家具

2.家具有名字和占地面积其中 席梦思占地4平米 衣柜占地2平米 餐桌占地1.5平米

3.将以上三件家具添加到房子里

4.打印房子的时候,户型 总面积,剩余面积,家具名称列表

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\n总面积:%.2f[剩余:%.2f]\n家具:%s"%(self.house_type,self.area,self.free_area,self.item_list))
    def add_item(self,item):
        print("要添加%s"%item)
        #1.判断家具面积
        if item.area >self.free_area:
            print("%s 的面积太大了,无法添加"%item.name)
            return
        #2.将加剧的名称添加到列表中
        self.item_list.append(item.name)
        #3.计算剩余面积
        self.free_area=self.free_area-item.area
# 1.创建家具
bed =HouseItem("席梦思",4)
chest =HouseItem("衣柜",2)
table =HouseItem("餐桌",1.5)
print(chest)
print(bed)
# 2.创建房子对象
my_home =House("两室一厅",60)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)

print(my_home)

案例二

一个对象的属性可以是另一个类创建的对象!

需求:

1.一个士兵许三多有一个 AK47

2.士兵可以开火

3.枪能够发射子弹

4.枪填装子弹–增加子弹数量

注意:

在定义属性的时候,如果不知道设置什么初始值,可以设置为None

  1. None关键字表示什么都没有

  2. 表示一个空对象,没有方法和属性,是一个特殊的变量

  3. 可以将None赋值给任何一个变量

class Gun:
    def __init__(self,model):
        #1. 枪的型号
        self.model =model
        #2. 子弹数量
        self.bullet_count =0
    def add_bullet(self,count):
        self.bullet_count+=count
    def shoot(self):
        #1.判断子弹数量
        if self.bullet_count<=0:
            print("[%s]没有子弹了..."%self.model)
            return
        #2.发射子弹 -1
        self.bullet_count -=1
        #3. 提示发射信息
        print("[%s] 突突突...[%d]"%(self.model,self.bullet_count))

class Soldier:
    def __init__(self,name):
        #1.新兵姓名
        self.name =name
        #2.枪 新兵没有枪
        self.gun =None
    def fire(self):
        #1.判断士兵是否有枪
        if self.gun ==None:
       #if self.gun is None:
            print("[%s]还没有枪..."%self.name)
            return
        #2.高喊口号
        print("冲啊...[%s]"%self.name)
        #3.让枪装填子弹
        self.gun.add_bullet(50)
        #4.让枪发射子弹
        self.gun.shoot()

#1. 创建枪类对象
ak47 =Gun("AK47")
#2.假设每一个新兵都没有枪 创建许三多
xusanduo =Soldier("许三多")
xusanduo.gun=ak47
xusanduo.fire()
print(xusanduo)

python的身份运算符None 在python中针对None比较时,建议使用is判断

运算符 描述 示例
is is是判断两个标识符是不是引用同一个对象 x is y ,类似id(x)==id(y)
is not is not 是判断两个标识符是不是引用不同的对象 x is not y ,类似id(a) !=id(b)

is 与==区别:

  1. is用于判断两个变量引用对象是否为同一个

  2. ==用于判断引用变量的值是否相等

私有属性和私有方法

应用场景及定义方式,在实际开发中,对象的某些属性或方法可能只希望在对象的内部被使用,而不希望在外部访问到。

  1. 私有属性就是对象不希望公开属性
  2. 私有方法就是对象不希望公开方法

定义方法: 在定义属性或方法时,在属性名或方法名前增加两个下划线,定义就是私有属性或方法。

class Women:
    def __init__(self,name):
        self.name =name
        self.__age =18
    def secret(self):
        #在对象的方法内部是可以访问对象的私有属性的
        print("%s的年龄是%d"%(self.name,self.__age))
xiaofang =Women("小芳")
print(xiaofang.name)
#私有属性,在外界不能够被访问,但是在内部是可以访问的
#方法也一样 __secret(self): 同样在外部不允许访问
# print(xiaofang.__age)
xiaofang.secret()

继承

单继承和多继承

面向对象的三大特性: 封装 继承 多态

封装:根据职责将属性和方法封装到一个抽象的类中

继承:实现代码的重用 相同的代码不需要重复的编写

多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

继承的概念:子类拥有父类的全部方法和属性

继承的语法:

class 类名(父类名)pass
  1. 子类继承父类,可以直接享受父类封装好的方法,不需要再次开发

  2. 子类中应该根据职责,封装子类特有的属性和方法

class Animals:
    def eat(self):
        print("吃")
    def drink(self):
        print("喝")
    def run(self):
        print("跑")
    def sleep(self):
        print("睡")
class dog(Animals):
    def bark(self):
        print("汪汪叫")
#创建一个对象 -狗对象
wangcai =dog()
wangcai.eat()
wangcai.drink()
wangcai.sleep()
wangcai.bark()

专业术语:

  1. Dog类是Animals的子类,Animals类是Dog类的父类,Dog类从Animals类继承
  2. Dog类是Animal类的派生类,Animals类是Dog类的基类,Dog类从Animals类派生

继承的传递性,子类拥有父类的全部特性,同时也继承有父类的父类的全部特性

class Animals:
    def eat(self):
        print("吃")
    def drink(self):
        print("喝")
    def run(self):
        print("跑")
    def sleep(self):
        print("睡")
class dog(Animals):
    def bark(self):
        print("汪汪叫")
class xiaotianquan(dog):
    def fly(self):
        print("我会飞!")
#创建一个对象 -wangcai 
wangcai =xiaotianquan()
wangcai.eat()
wangcai.drink()
wangcai.sleep()
wangcai.bark()
wangcai.fly()

继承的传递性的注意事项,一定要注意是否直接的继承关系,没有直接的继承的关系,那么就不能调用

方法的重写

  1. 子类拥有父类的所有方法和属性
  2. 子类继承自父类 可以直接 享受父类中已经封装好的方法,不需要再次开发
  3. 当父类的方法不能满足子类的需求时,可以对方法进行重写

实现方式:在子类中重新编写一下父类的方法,简单来说就是重新定义一个重名的函数,直接覆盖掉就行。

class Animals:
    def eat(self):
        print("吃")
    def drink(self):
        print("喝")
    def run(self):
        print("跑")
    def sleep(self):
        print("睡")
class dog(Animals):
    def bark(self):
        print("汪汪叫")
class xiaotianquan(dog):
    def fly(self):
        print("我会飞!")
    def bark(self):
        print("叫的不一样")
xtq =xiaotianquan()
#如果子类中重写了父类的方法
#在使用子类对象调用方法时,会调用子类中封装的方法
xtq.bark()

对父类方法进行拓展:

在父类的方法上进行拓展可以使用

super().父类的方法

class Animals:
    def eat(self):
        print("吃")
    def drink(self):
        print("喝")
    def run(self):
        print("跑")
    def sleep(self):
        print("睡")
class dog(Animals):
    def bark(self):
        print("汪汪叫")
class xiaotianquan(dog):
    def fly(self):
        print("我会飞!")
    def bark(self):
        #1. 针对子类特有的需求编写代码
        print("不一样的叫法")
        #2. 使用 super().调用原本在父类中封装的方法
        super().bark()
        #3. 增加其他子类代码
        print("#%$^^*^%&$%#$#$%")
xtq =xiaotianquan()
#如果子类中重写了父类的方法
#在使用子类对象调用方法时,会调用子类中封装的方法
xtq.bark()

私有属性和私有方法

  1. 子类对象不能在自己的方法内部直接访问父类的私有属性或方法
  2. 子类对象可以通过父类的公有方法简介访问到私有属性或私有方法
  3. 私有属性,方法是对象的隐私,不对外公开,外界和子类都不能访问
class A:
    def __init__(self):
        self.num1 =100
        self.__num2 =200
    def __test(self):
        print("私有方法 %d %d"%(self.num1,self.__num2))
class B(A):
    def demo(self):
        #1.在子类的对象方法中,不能访问父类的私有属性
        # print("访问父类的私有属性%d"%(self.__num2))
        #2.子类对象无法调用,调用父类的私有方法

        # self.__test()
        pass
#创建一个子类对象
b =B()
print(b)
#在外界不能直接访问对象的私有属性/调用私有方法
# print(b.__num2)
# b.__test()

多继承,子类拥有一个父类被称为单继承,但是子类可以拥有多个父类

class 子类名(子类名1,子类名2.....)
	pass
class A:
    def test(self):
        print("test 方法")
class B:
    def demo(self):
        print("demo方法")
class C(A,B):
    """多继承可以让子类对象同时具有多个父类的属性和方法"""
    pass
#创建一个子类对象
c =C()
c.test()
c.demo()

使用多继承的注意事项,存在同名的方法时应当注意避免多继承

python的MRO –方法搜索顺序(可以搜索一下哪个优先调用)

print(c.__mro__)

多态

不同的子类对象调用相同的父类方法,产生不同的执行结果

  1. 多态可以增加代码的灵活度
  2. 以继承和重写父类方法为前提
  3. 是调用方法的技巧,不会影响到类的内部设计

多态案例演练:

需求:

  1. 在dog类中封装方法game (普通狗只是简单的玩耍)
  2. 定义xiaotianquan继承自dog并重写game方法 (哮天犬需要在天上玩耍)
  3. 定义Person类,并且封装一个和狗玩的方法 在方法内部,(直接让狗对象调用game方法)
class Dog(object):
    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 Person(object):
    def __init__(self,name):
        self.name =name
    def game_with_dog(self,dog):
        print("%s 和%s快乐的玩耍"%(self.name,dog.name))
    #让狗玩耍
        dog.game()
#1.创建一个狗对象
# wangcai =Dog("旺财")
wangcai =xiaotianquan("飞天旺财")
#2.创建一个小明对象
xiaoming =Person("小明")
#3. 让小明调用狗玩的方法
xiaoming.game_with_dog(wangcai)

类的结构

面向对象开发–实例

  1. 首先要先设计类。

  2. 使用 类名() 创建对象

    1. 在内存中为对象分配空间
    2. 调用实例化方法 __init__为对象初始化
  3. 对象创建后,内存中就有了一个对象的实实在在的实例

类名() =====> __init__定义(实例属性)

对象名.方法名() ====> 实例方法(self)

紧接着对象去调用属性

每一个对象都有自己的独立的内存空间,保存各自不同的属性

类是一个特殊的对象

类属性

  1. 类属性是类对象中定义的属性
  2. 通常用来记录与这个类相关的特征
  3. 类属性不会用于记录具体对象的特征
class Tool(object):
    #使用赋值语句,定义类属性,记录创建工具对象的总数
    count =0
    def __init__(self,name):
        self.name =name
        #让类属性的值+1
        Tool.count +=1
#1.创建工具对象
tool1 =Tool("斧头")
tool2 =Tool("榔头")
#2. 输出工具对象的总数
print(Tool.count)

属性获取机制: python中属性的获取存在一个向上查找的机制

tool1.count

  1. 首先在对象内部 查找对象属性
  2. 没有找到就会向上寻找类属性

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

类方法和静态方法

语法如下:

@classmethod
def 类方法名(cls):
	pass

类方法必须要用到修饰器 @classmethod来进行标识,告诉解释器这是一个类方法,类方法的第一个参数应该是cls

  1. 由哪一个类调用的方法,方法内的cls就是哪一个类的引用
  2. 这个参数和示例方法的第一个参数是self类似
  3. 提示,使用其他名称也可以,不过习惯用cls

通过对类名 调用类方法 调用方法时,不需要传递cls参数

在方法内部,也可以通过cls访问类的属性,也可以通过cls调用其他类的方法

示例需求:

  1. 定义一个工具类
  2. 每件工具都有自己的name
  3. 需求–在类封装一个show_tool_count 的类方法,输出使用当前这个类,创建对象的个数
class Tool(object):
    count =0
    @classmethod
    def show_tool_count(cls):
        print("工具对象的数量%d"%cls.count)
    def __init__(self,name):
        self.name =name
        #让类属性的值+1
        Tool.count +=1
#创建工具对象
tool1 =Tool("斧头")
tool2 =Tool("锄头")
#调用类方法
Tool.show_tool_count()

静态方法:

需要加上@staticmethod关键字

class Dog(object):
	@staticmethod
	def run():
        #不访问实例属性/类属性
		print("小狗要跑。。。。")
#通过类名.调用静态方法
#不需要创建对象,可以直接第哦啊用你
Dog.run()

示例:

  1. 设计一个Game类

  2. 属性

    1. 定义一个类属性,top_score 记录游戏的历史最高分
    2. 定义一个实例属性 play_name 记录当前游戏的玩家姓名
  3. 方法

    1. 静态方法 show_help 显示游戏帮助信息
    2. 类方法 show_top_score 显示历史最高分
    3. 实例方法 start_grame 开始当前玩家的游戏
  4. 主程序步骤

    1. 查看帮助信息
    2. 查看历史最高分
    3. 创建游戏对象,开始游戏
class Game(object):
    #历史最高分
    top_score =0
    #实例方法
    def __init__(self,player_name):
        self.player_name = player_name
    #静态方法
    @staticmethod
    def show_help():
        print("帮助信息")
    #类方法
    @classmethod
    def show_top_score(cls):
        print("历史记录%d"%cls.top_score)
    def sart_game(self):
        print("%s 开始游戏啦..."%self.player_name)
#1.查看游戏帮助信息
Game.show_help()
#2.查看历史最高分
Game.show_top_score()
#3.创建游戏对象
game = Game("小明")
game.sart_game()

案例总结

  1. 案例方法–方法内部需要访问实例属性

    1. 案例方法内部可以使用赖明访问类属性
  2. 类方法 –方法内部只需要访问类属性

  3. 静态方法 –方法内部,不需要访问实例属性和类属性

单例

单例设计模式 让类创建对象,在系统中只有唯一的一个实例,每一次执行 类名() 返回的对象,内存地址是相同的

__new__方法是一个由object基类提供的内置的静态方法,为对象分配空间,返回对象引用

__init__方法是初始化,定义实例属性

重写 __new__方法的代码非常固定

重写 __new__方法一定要 return super().__new__(cls)

否则 __new__是一个静态方法,在调用是需要注意注定传递cls参数

class MusicPlayer(object):
    def __new__(cls, *args, **kwargs):
        #1.创建对象时,new方法会被自动调用
        print("创建方法。分配空间")
        #2.为对象分配空间
        result = super().__new__(cls)
        #3.返回对象的引用
        return result
    def __init__(self):
        print("播放器初始化")
#创建播放器对象
play =MusicPlayer()
print(play)

单例设计模式设计演练

class MusicPlayer(object):
    #记录第一个被创建的对象的引用
    instance =None
    def __new__(cls, *args, **kwargs):
        #1.创建类属性是否为空对象
        if cls.instance is None:
            #2.调用父类的方法,为第一个对象分配空间
            cls.instance =super().__new__(cls)
        return cls.instance
play1 =MusicPlayer()
print(play1)
play2 =MusicPlayer()
print(play2)

写了这么多方法,只要能认真的看完,我相信一定会有质的飞跃的!!!!!


版权声明:本文为weixin_46900108原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。