面向对象的学习

以前的python学习都是面向过程的,过程跟函数的区别就在于过程是没有返回值,函数有返回值的。
之前学习的变量、数据、函数都是对象。
对象=属性+方法。
在设计一个类的时候,通常要满足以下三要素:
1.类名 这类事物的名字,满足大驼峰命名法
2.属性 这类事物具有什么样的特征
3.方法 这类事物具有什么样的行为

类名的确定

名词提炼法 分析 整个业务流程,出现的 名词,通常就是找到的类

属性和方法的确定

对象的特征描述,通常可以定义成 属性
对象具有的行为(动词),通常可以定义成 方法
利用好dir()函数,在学习时很多内容就不用死记硬背了。

定义简单的类

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

#定义一个猫类 Cat
#定义两个方法eat和drink
class Cat:
    """这是一个阿猫"""
    def eat(self):
    # 哪一个对象调用的方法,self就是哪一个对象的引用
        print('%s 要吃饭!'% self.name)
    def drink(self):
        print('猫猫要喝水!')
tom = Cat()
tom.name="Tome"
tom.drink()
tom.eat()
lazy_cat = Cat()
lazy_cat.name='大懒猫'
lazy_cat.drink()
lazy_cat.eat()

在面向对象的开发中,引用的概念同样适用。
在这里可以更好的理解一下self的作用。
在类封装的方法内部,self就表示当前调用方法的对象自己
调用方法时程序员不需要传递self参数
在方法内部
1.可以通过self.访问对象的属性
2.也可以通过self.调用其他的对象方法
使用类名()创建对象的时候,会自动调用初始化方法__init__
__init__方法是专门用来定义一个类 具有哪些属性的方法!

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

在__init__方法内部使用,self.属性名=属性的初始值,就可以定义属性
定义属性之后,在使用Cat类创建的对象,都会拥有该属性。

class Cat:
    """这是一个阿猫"""
    def __init__(self):
        print('这是一个初始化方法')
        self.name = "Tom"
tom = Cat()

print(tom.name)

这样的初始化产生的效果是所有的Cat类的猫的名字都是Tom,这就存在了很大的问题,所以我们要给初始化方法增加一个形参。

__del__方法:对象被从内存中销毁前,会被自动调用(使用频率不高)
__str__方法:返回对象的描述信息,print函数输出使用

class Cat:
    """这是一个阿猫"""
    def __init__(self,new_name):
        
        self.name = new_name
        print('%s 来了'% self.name)

    def __del__(self):
        print('%s 我去了'% self.name)
tom = Cat("Tom")
print(tom.name)

print('-'*50)

生命周期:
一个对象从调用类名()创建,生命周期开始
一个对象的__del__方法一旦被调用,生命周期结束
在对象的生命周期内,可以访问对象属性,或者让对象调用方法
············································
在python中使用pritn输出对象变脸,默认情况下,会输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址
如果在开发中,希望使用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):
        #__str__必须返回一个字符串
        return '我是小猫[%s]'% self.name
tom = Cat("Tom")
print(tom)

面向对象封装案例

1.封装是面向对象变成的一大特点
2.面向对象的第一步–将属性和方法封装到一个抽象的类中
3.外界使用类创建对象,让对象调用方法
4.对象方法的细节都被封装到类的内部

小明爱跑步

需求
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 -= 0.5
   
    def eat(self):
        print('%s爱吃东西,吃东西会长胖'% self.name)
        self.weight += 1.0

xiaoming = Person('小明', 75.0)

xiaoming.run()
xiaoming.eat()
xiaoming.eat()
xiaoming.eat()
xiaoming.eat()
xiaoming.eat()
xiaoming.eat()
xiaoming.eat()
xiaoming.eat()

print(xiaoming)
摆放家具

需求
1.房子(House)有户型、总面积和家具名称列表
(新房子没有任何家具)
2.家具(HouseItem)有名字和占地面积,其中
席梦思(bed)占地4平米
衣柜(chest)占地2平米
餐桌(table)占地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):
        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 -= item.area


#1.创建家具
bed = HouseItem('席梦思',40)
chest = HouseItem('衣柜',2)
table = HouseItem("餐桌",15)



print(bed)
print(chest)
print(table)

#2.创建房子对象
my_home = House('两室一厅',80)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)

print(my_home)

none关键字

士兵突击

需求
1.士兵许三多有一把AK47
2.士兵可以开火
3.枪可以发射子弹
4.枪装填子弹–增加子弹数量

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:
            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.gun)

身份运算符
身份运算符用于比较两个对象的内存地址是否一致–是否是对同一个对象的引用
在python中针对None比较时,建议使用is判断

is:是判断两个标识符是不是引用同一个对象
x is y,类似id(x) == id(y)
is not:判断两个标识符是不是引用不同对象
x is y,类似id(x) != id(y)
is与= =的区别

私有属性和私有方法

在实际开发中,对象的某些属性或方法可能只希望在对象的内部被使用,而不希望在外界被访问到
私有属性就是对象不希望公开的属性
私有方法就是对象不希望公开的方法
定义方式:在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法。

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.__age)

xiaofang.secret()

但是如果在大类前面加一个下划线的话也能调用,所以在python中是没有真正意义上的私有的。

继承

分为单继承和多继承。
面向对象的三大特性
1.封装:根据职责将属性和方法封装到一个抽象的类中
2.继承:实现代码的重用,相同的代码不需要重复的编写
3.多态:不同的对象调用相同的方法,产生不同的执行效果,增加代码的灵活度
在子类的括号中加入括号的类名称,子类拥有父类的所有属性和方法。

class Animal:
    def eat(self):
        print('吃---')
    
    def drink(self):
        print('喝---')
    
    def run(self):
        print('跑---')
   
    def sleep(self):
        print('睡---')



class Dog(Animal):
    #def eat(self):
    #    print('吃')
    #
    #def drink(self):
    #    print('喝')
    
    #def run(self):
    #    print('跑')
   
    #def sleep(self):
    #    print('睡')
    def bark(self):
        print('汪汪叫')


# 创建一个对象   狗对象
wangcai = Dog()

wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()

继承也具有传递性。
对父类方法进行扩展
1.在子类中可以对父类进行重新编写,可以直接进行覆盖。
2.在需要的位置使用super().父类方法来调用父类方法的执行
3.代码其他的位置针对子类的需求,编写子类特有的代码实现

关于super
1.在python中super是一个特殊的类
2.super()就是使用super类创建出来的对象
3.最长使用的场景就是在重写父类方法时,调用父类中封装的方法实现
父类公有方法中,可以调用父类的私有方法
子类对象方法中,不能直接访问父类的私有属性
子类对象方法中,不能直接调用父类的私有方法
子类对象可以通过父类公有方法 间接访问到私有属性私有方法
···························`···············
多继承:子类可以拥有多个父类,并且具有所有父类的属性和方法

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()
多继承的使用注意事项

如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢?
提示:开发时,应该尽量避免这种容易产生混淆的情况!—如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承。
谁在前面调用谁。
使用内置方法__mro__可以查看方法搜索顺序
MRO是method resolution order,主要用于在多继承时判断方法、属性的调用路径。

多态

不同的子类对象调用相同的父类方法,产生不同的执行结果。
1.多态可以增加代码的灵活度
2.以继承和重写父类方法为前提
3.是调用方法的技巧,不会影响到类的内部设计

class Dog(object):

    def __init__(self,name):
        self.name = name

    def game(self):
        print('%s蹦蹦跳跳的玩耍...'% self.name)

    
class XiaoTianDog(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 = XiaoTianDog('飞天旺财')

# 2.创建一个小明对象
xiaoming = Person('小明')

# 3.让小明调用和狗玩的方法
xiaoming.game_with_dog(wangcai)

1.在Dog类中封装方法game
普通狗只是简单的玩耍
2.定义XiaoTianDog继承自Dog,并且重写game方法
哮天犬需要在天上玩耍
3.定义Person类,并且封装一个和狗玩的方法
在方法内部,直接让狗对象调用game方法

class Tool(object):

    # 使用赋值语句定义类属性,记录所有工具对象的数量
    count = 0

    def __init__(self,name):
        self.name = name

        # 让类属性的值+1
        Tool.count += 1


# 1.创建工具对象
tool1 = Tool('斧头')
tool2 = Tool('榔头')
tool3 = Tool('水桶')

# 2.输出工具对象的综述
print(Tool.count)

类属性的概念和使用
类属性就是给类对象中定义的属性
通常用来记录与这个类相关的特征
类属性不会用于记录具体对象的特征

类方法就是针对类对象定义的方法
在类方法内部可以直接访问类属性或者调用其他的类方法

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

类方法需要修饰器@classmethod来表示,告诉解释器这是一个类方法
类方法的第一个参数应该是cls
1.由哪一个类调用的方法,方法内的cls就是哪一个类的引用
2.这个参数和实例方法的第一个参数是self类似
3.提示 使用其他名称也可以,不过习惯使用cls参数

通过类名,调用类方法,调用方法时,不需要传递cls参数
在方法内部
1.可以通过cls.访问累的属性
2.也可以通过cls.调用其他的类方法
需求
1.定义一个工具类
2.每件工具都有自己的name
3.需求–在类封装一个show_tool_count的类方法,输出使用当前这个类,创建的对象个数

class Tool(object):
 
    # 使用赋值语句定义类属性,记录所有工具对象的数量
    count = 0

    def __init__(self,name):
        self.name = name

        # 让类属性的值+1
        Tool.count += 1


# 1.创建工具对象
tool1 = Tool('斧头')
tool2 = Tool('榔头')
tool3 = Tool('水桶')

# 2.输出工具对象的综述
print(Tool.count)
静态方法

在开发时,如果需要在类中封装一个方法,这个方法:
1.既不需要访问实例属性或者调用实例方法
2.也不需要访问类属性或者调用类方法
这个时候,可以把这个方法封装成一个静态方法。

案例分析

1.设计一个Game类
2.属性:
定义一个 类属性 top_score记录游戏的历史最高分
定义一个实例属性player_name记录当前游戏的玩家姓名
3.方法:
静态方法 show_help显示游戏帮助信息
类方法 show_top_score显示历史最高分
实例方法 start_game开始当前玩家的游戏
4.主程序步骤
1)查看游戏帮助
2)查看历史最高分
3)创建游戏对象、开始游戏

class Game:

    # 历史最高分 = 0
    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 start_game(self):
        print('%s开始游戏啦。。。'% self.player_name)

# 1.查看游戏的帮助信息
Game.show_help()

# 2.查看历史最高分
Game.show_top_score()

# 3.创建游戏对象,开始游戏
game = Game('小明')
game.start_game()

案例小结
1.实例方法–方法内部需要访问实例属性
实例方法内部可以使用类名,访问类属性
2.类方法–方法内部需要访问类属性
3.静态方法–方法内部,不需要访问实例属性类属性

单例(单个实例)

目标
单例设计模式
__new__方法
python中的单例

单例设计模式

设计模式
设计模式前人工作的总结和提炼,通常被人们广泛流传的设计模型都是针对某一特定问题的成熟解决方案
使用设计模型可以为了可重用代码、让代码更容易被他人理解、保证代码可靠性
单例设计模式
目的——让类创建的对象,在系统中只有唯一的一个实例
每一次执行类名()返回的对象,内存地址是相同的
单例设计模式的应用场景
音乐播放对象
回收站对象
打印机对象
__new__方法
使用类名()创建对象时,python的解释器首先会调用__new__方法为对象分配空间
__new__是一个由object基类提供的内置的静态方法,主要作用有两个:
1)在内存中为对象分配空间
2)返回对象的引用
python解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法

重写__new__方法的代码非常固定
重写__new__方法一定要 return super().new(cls)

否则python解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
注意:__new__是一个静态方法,在调用时需要主动传递cls参数

class MusicPlayer():


    def __new__(cls,*args,**kwargs):

        # 1.创建对象时,new方法会被自动调用
        print('创建对象,分配空间')

        # 2.为对象分配空间
        instance = super().__new__(cls)

        # 3.返回对象的引用
        return instance

    def __init__(self, *args,**kwargs):
        # 一个*表示是多值的元组参数,两个*表示是多值的自由参数
        
        print('音乐播放器初始化')


# 创建播放器对象
player = MusicPlayer()

print(player)

单例——让类创建的对象,在系统中只有为一个的一个实例
1.定义一个类属性,初始值是None,用于记录单例对象的引用
2.重写__new__方法
3.如果类属性is None,调用父类方法分配空间,并在类属性中记录结果
4.返回类属性中记录的对象引用

# 得到的内存地址永远都是相同的
class MusicPlayer():

    # 记录第一个被创建对象的引用
    instance = None

    # 记录是否执行过初始化动作
    init_flag = False

    def __new__(cls, *args, **kwargs):

        # 1.判断类属性是否是空对象
        if cls.instance is None:


        # 2.调用父类的方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)

        
        # 3.返回类属性保存的对象引用
        return cls.instance
    
    def __init__(self):

        # 1.判断是否执行过初始化动作
        if MusicPlayer.init_flag:
            return

        # 2.如果没有执行过,在执行初始化动作

        print('初始化播放器')

        # 3.修改类属性的标记
        MusicPlayer.init_flag = True

        
# 创建多个对象
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

得到的内存地址永远都是相同的

异常
程序在运行时,如果python解释器遇到一个错误,会停止程序的执行,并且提示一些错误信息,这就是**异常**
程序停止执行并且提示错误信息这个动作,我们通常称之为:抛出异常

捕获异常
简单的捕获异常语法
在程序开发中,如果对某些代码的执行不能确定是否正确,可以增加try(尝试)来捕获异常
捕获异常最简单的语法格式:

try:
	尝试执行的代码
except:
	出现错误的处理

try尝试,下方编写要尝试代码,不确定是否能够正常执行的代码
except如果不是,下方编写尝试失败的代码

try:

    # 提示用户输入一个整数
    num = int(input('输入一个整数:'))

    # 使用 8 除以用户输入的整数并且输出
    result = 8 / num

    print(result)

# 捕获已知错误
except ValueError:
    print('请输入正确的格式')

# 捕获未知错误
except Exception as result:
    print('未知错误 %s'% result)
else:
	# 没有异常才会执行的代码
finally:
	# 无论是否异常,都会执行的代码
	print('不论是否有异常,都会执行的代码')

在主函数中增加异常捕获,只要出现异常就会传递到主函数的异常捕获中,这样就不需药在打吗中,增加大量的一场捕获,能够保证代码的整洁。

抛出raise异常
在开发中,出了代码执行出错python解释器抛出异常之外

还可以 根据应用程序特有 的业务需求主动抛出异常
python中提供了一个exception 异常类
在开发时,如果满足特定业务需求时,希望抛出异常,可以:
1.创建一个exception的对象
2.使用raise关键字抛出异常对象
实例需求:
1.定义input_password函数,提示用户输入密码
2.如果用户输入长度<8,抛出异常
3.如果用户输入长度≥8,返回输入的密码

def input_password():

    # 1.提示用户输入密码
    pwd = input('请输入密码:')


    # 2.判断密码长度 ≥ 8,返回用户输入的密码
    if len(pwd) >= 8:
        return pwd
    # 3.如果 < 8,主动抛出异常。
    print('主动抛出异常')
    # 1> 创建异常对象
    ex = Exception('密码长度不够')# 指定异常描述信息
    
    # 2> 主动抛出异常
    raise ex


# 提示用户输入密码
try:
    print(input_password())
except Exception as result:
    print(result)

如果想主动抛出异常,要先创建一个异常对象。
然后使用raise函数,将异常抛出,然后使用try捕获函数。

模块

从模块导入所有工具
from 模块名1 import *
(但是不推荐 )
模块的搜索顺序
python解释器在导入模块时,会:
1.搜索当前目录制定模块名的文件,如果有就直接导入
2.如果没有,在搜索系统目录
在开发时,给文件起名,不要和系统的模块文件重名。

原则

一个独立的 python 文件 就是一个 模块
在导入文件时,文件中 所有没有任何缩进的代码都会被执行一遍
!!!!__name__属性可以做到,测试模块的代码只在测试情况下被运行,而在被导入时不会被执行。!!!
__name__是python的一个内置属性,记录着一个字符串
如果是呗其他文件导入的,name__就是模块名
如果是当前执行的程序__name__是__main

包是一个包含多个模块的特殊目录
目录下有一个特殊的文件__init__.py
包名的命名方式和变量名一样,小写字母+_
好处
使用miport包名可以一次性导入包中所有的模块
案例:
1.新建一个hm_message的包
2.在目录下,新建两个文件send_message和receive_message
3.在send_message文件中定义一个send函数
4.在receive_message文件中定义一个receive函数
5.在外部直接导入hm_message的包
计算机的文件,就是存储在某种长期储存设备上的一段数据
长期存储设备包括:硬盘、U盘、移动硬盘、光盘
文件的作用
将数据长期保存下来,在需要的时候使用。
访问方式:
r:以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛出异常
w:以只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件。
a:以追加方式打开文件。如果该文件已经存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入。
r+:以读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常。
w+:以读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件。
a+:以读写方式打开文件。如果文件已经存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入。
如果文件太大,堆内存的占用会非常严重,所以使用readline的方法,一次只读取一行内容,并且执行完成后,会把文件指针移动到下一行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值