分类: python | 标签: python

Python面向对象笔记

发表于: 2024-09-17 22:32:33 | 字数统计: 5.6k | 阅读时长预计: 27分钟

dir()函数

使用内置函数 dir传入标识符/数据,可以查看对象内的 所有属性及方法

a = "1234"
print(dir(a))  # ['__add__', '__class__', '__contains__', '__delattr__'.......]

扩展

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

__new__ 创建对象时自动调用
__init__ 对象被初始化时自动调用
__del__ 对象被从内存中销毁前自动调用
__str__ 返回对象的描述信息

类和对象

定义类

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

方法和之前定义的函数一样,区别是第一个参数必须是self

注意:类名要符合驼峰命名

创建对象

变量 = 类名()

【例如】

class Cat:
    def eat(self):
        print('Eating')

    def drink(self):
        print('Drinking')
# 创建对象
a = Cat()
a.eat()
a.drink()

self参数

由 哪一个对象 调用方法,方法内的 self 就是 哪一个对象的引用,类似Java类中的this关键字.

调用方法时,不需要传递 self参数在方法内部
可以通过 self.访问对象的属性
也可以通过 self.调用其他的对象方法

【案例】

class Cat:
    def eat(self):
        print('%s Eating' % self.name)

    def drink(self):
        print('%s Drinking' % self.name)

    def sleep(self):
        print('Sleeping')
        self.eat() # 通过self调用其它方法

c = Cat()
c.name = 'tom'
c.eat()
c.drink()
c.sleep()

初始化方法

  • 在日常开发中,不推荐在 类的外部 给对象增加属性
  • 如果在运行时,没有找到属性,程序会报错。对象应该包含有哪些属性,应该 封装在类的内部
当使用 类名()创建对象时,会自动 执行以下操作:
1.为对象在内存中 分配空间 --创建对象
2.为对象的属性 设置初始值 --初始化方法( init )
这个 初始化方法 就是__init__方法,__init__ 是对象的内置方法

初始化方法是专门用来定义一个类 具有哪些属性的方法!

【案例】

class Cat:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print('%s %s Eating' % (self.name, self.age))

    def drink(self):
        print('%s Drinking' % self.name)


a = Cat('Jack', 22)
a.eat()
b = Cat('lucy', 18)
b.drink()

内置方法

__del__ 方法 对象被从内存中销毁前,会被 自动 调用 (了解)
__str__ 方法 返回对象的描述信息,print 函数输出使用 (类似Java的toString方法)

【案例】

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

    def __str__(self):
        return "我是猫咪【%s】" % self.name

a = Cat("汤姆") # a是一个全局变量
print(a)

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

在 封装的 方法内部,还可以让 自己的 使用其他类创建的对象属性 调用已经 封装好的方法

身份运算符

身份运算符用于 比较 两个对象的 内存地址 是否一致 –是否是对同一个对象的引用。

在Python 中针对None比较时,建议使用is判断

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

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

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

arr1 = [1, 2, 3]
arr2 = [1, 2, 3]

print(arr1 is arr2) # False
print(arr1 == arr2) # True

私有属性和私有方法

应用场景

在实际开发中,对象 的 某些属性或方法 可能只希望 在对象的内部被使用,而 不希望在外部被访问到
私有属性 就是 对象 不希望公开的 属性
私有方法 就是 对象 不希望公开的 方法

定义方式

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

class Cat:
    def __init__(self,name,age):
        self.name = name
        self.__age = age
        self.__secret()
    def print_age(self):
        print(self.__age)

    def __secret(self):
        print("secret")

a = Cat("tom",18)
# a.__secret() # error
# print(a.__age) # error
a.print_age()

继承

面向对象三大特性

封装 根据 职贵 将 属性 和 方法 封装 到一个抽象的 类中
继承 实现代码的重用,相同的代码不需要重复的编写
多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

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

image-20240916104417068

继承的语法

class 类名(父类名): 
  pass
  • 子类 继承自 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发

  • 子类 中 根据 职责,封装 子类特有的 属性和方法

子类 = 派生类 ,父类 = 基类

继承的传递性

C类从 B类继承,B类又从A类继承。那么C类就具有 B类和 A类的所有属性和方法

方法重写

子类 拥有 父类 的所有 方法 和 属性
子类 继承自 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发

应用场景

当 父类 的方法实现不能满足子类需求时,可以对方法进行 重写(override)

重写父类方法有两种情况:
1)覆盖父类的方法
如果在开发中,父类的方法实现 和 子类的方法实现,完全不同就可以使用 覆盖 的方式,在子类中 重新编写 父类的方法实现
具体的实现方式,就相当于在 子类中 定义了一个和【父类同名的方法】并且实现重写之后,在运行时,只会调用 子类中重写的方法,而不再会调用 父类封装的方法

2)对父类方法进行扩展
如果在开发中,子类的方法实现 中 包含 父类的方法实现。父类原本封装的方法实现 是 子类方法的一部分就可以使用 扩展 的方式
1.在子类中重写父类的方法
2.在需要的位置使用 super()父类方法 来调用父类方法的执行
3.代码其他的位置针对子类的需求,编写 子类特有的代码实现

父类的私有属性和私有方法

a.子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性 或 私有方法
b.子类对象 可以通过 父类 的 公有方法 间接 访问到 私有属性 或 私有方法

私有属性、方法 是对象的隐私,不对外公开,外界以及 子类 都不能直接访问
私有属性、方法 通常用于做一些内部的事情

多继承

子类 可以拥有 多个父类,并且具有 所有父类 的 属性 和 方法。
例如:孩子 会继承自己 父亲 和 母亲 的 特性

语法

class 子类名(父类名1,父类名2...)
  pass

注意:如果父类之间 存在 同名的属性或者方法,应该尽量避免 使用多继承

image-20240916143441100

多态

面向对象三大特性
a. 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类中
   定义类的准则
b.继承 实现代码的重用,相同的代码不需要重复的编写
   设计类的技巧
   子类针对自己特有的需求,编写特定的代码
c.多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
  多态 可以 增加代码的灵活度
  以 继承 和 重写父类方法 为前提
  是调用方法的技巧,不会影响到类的内部设计

术语

a. 创建出来的 对象 叫做 类 的 实例
b. 创建对象的 动作 叫做 实例化
c. 对象的属性 叫做 实例属性
d. 对象调用的方法 叫做 实例方法

在程序执行时:
1).对象各自拥有自己的 实例属性
2).调用对象方法,可以通过  self. 访问自己的属性和调用自己的方法

结论
1)每一个对象 都有自己 独立的内存空间,保存各自不同的属性
2)多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部

类对象

类是一个特殊的对象

在程序运行时,类 同样 会被加载到内存
在Python 中,类是一个特殊的对象--类对象
在程序运行时,类对象 在内存中 只有一份,使用 一个类 可以创建出 很多个对象实例
除了封装 实例 的 属性 和 方法外,【类对象】 还可以拥有自己的 【属性】 和 【方法】

通过 类名. 的方式可以访问类的属性或者调用类的方法

image-20240916153445680

【案例】

class Tool:
    # 使用赋值语句,定义类属性,记录创建工具对象的总数
    count = 0

    def __init__(self, name):
        self.name = name
        Tool.count += 1

tool1 = Tool("111")
tool2 = Tool("222")
tool3 = Tool("333")
print(Tool.count)

image-20240916154058358

类方法和静态方法

类方法

a.类属性 就是针对 类对象 定义的属性
使用 赋值语句 在 class 关键字下方可以定义 类属性
类属性 用于记录 与这个类相关 的特征
b.类方法 就是针对 类对象 定义的方法
在 类方法 内部可以直接访问 类属性 或者调用其他的 类方法

语法

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

a.类方法需要用 修饰器 @classmethod 来标识,告诉解释器这是一个类方法
b.类方法的 第一个参数 应该是 cls
    由 哪一个类 调用的方法,方法内的 cls 就是 哪一个类的引用
    这个参数和 实例方法 的第一个参数是self 类似
    使用其他名称也可以,不过习惯使用cls
c.通过 类名.调用 类方法,调用方法时,不需要传递cls 参数
d.在方法内部 通过 cls.访问类的属性或通过 cls.调用其他的类方法

【案例】

class Tool:
    # 使用赋值语句,定义类属性,记录创建工具对象的总数
    count = 0

    @classmethod
    def show_tool_count(cls):
        print("总数 %s" % cls.count)

    def __init__(self, name):
        self.name = name
        Tool.count += 1

tool1 = Tool("111")
tool2 = Tool("222")
tool3 = Tool("333")

Tool.show_tool_count() # 总数 3

静态方法

在开发时,如果需要在 类 中封装一个方法,这个方法:
    既 不需要 访问 实例属性 或者调用 实例方法。
    也 不需要 访问 类属性 或者调用 类方法
那么就可以定义成一个静态方法

语法

@staticmethod
def 静态方法名(): 
    pass

静态方法 需要用 修饰器 @staticmethod 来标识
通过 类名. 调用静态方法

【案例】

class Tool:

    @staticmethod
    def sum(a,b):
        return a + b

print(Tool.sum(1,2))

小结

实例方法: 可以访问【实例属性】并且可以通过 类名. 访问【类属性】
类方法: 只能访问【类属性】
静态方法: 不能访问类属性和实例属性

捕获异常

简单的捕获语法

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

【案例】

try:
    num = int(input("Enter a number: "))
except:
    print("请输入正确的数字")

错误类型捕捉

在程序执行时,可能会遇到 不同类型的异常,并且需要 针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了

语法

try: 
    #尝试执行的代码
    pass 
except 错误类型1: 
    #针对错误类型1执行的代码
    pass
except 错误类型2: 
    #针对错误类型2执行的代码
    pass
except Exception as result:  # 针对未知错误执行的代码
    print("未知错误 %s" % result)

异常捕获完整语法

try: 
    #尝试执行的代码
    pass 
except 错误类型1: 
    print("针对错误类型1执行的代码")
except 错误类型2: 
    print("针对错误类型2执行的代码")
except Exception as result:
    print("未知错误 %s" % result)
else: 
    print("没有异常才会执行的代码")
finally: 
    print("无论是否异常都会执行的代码")

异常的传递

当 函数/方法 执行 出现异常,会 将异常传递 给 函数/方法 的 【调用一方】
如果 传递到主程序,仍然 没有异常处理,程序才会被终止

在开发中,可以在主函数中增加 异常捕获
而在主函数中调用的其他函数,只要出现异常,都会传递到主函数的 异常捕获 中
这样就不需要在代码中,增加大量的 异常捕获,能够保证代码的整洁

【案例】

def demo1():
    return int(input("请输入一个整数"))

def demo2(): 
    return demo1()

try:
    print(demo2())
except ValueError: 
    print("请输入正确的整数")
except Exception as result: 
    print("未知错误 %s" % result)

抛出raise异常

在开发中,除了 代码执行出错Python 解释器会 抛出 异常之外
还可以根据 应用程序 特有的业务需求 主动抛出异常

Python 中提供了一个Exception 异常类在开发时,如果满足 特定业务需求时,希望 抛出异常,可以:
a.创建一个 Exception 的 对象
b.使用 raise 关键字 抛出 异常对象

【案例】

def demo1():
    a = int(input("请输入一个整数"))
    if a < 10:
        ex = Exception("请输入大于10的数字")
        raise ex # 主动抛出异常
    return a

try:
    print(demo1())
except ValueError:
    print("请输入正确的整数")
except Exception as result:
    print("未知错误 %s" % result)

模块

每一个以扩展名py结尾的Python 源代码文件都是一个 模块
模块名 同样也是一个 标识符,需要符合标识符的命名规则
在模块中定义的 全局变量、函数、类都是提供给外界直接使用的 工具
模块 就好比是 工具包,要想使用这个工具包中的工具,就需要先 导入 这个模块

模块的两种导入方式

import导入

import 模块名1,模块名2

每个导入需要独占一行

import 模块1
import 模块2

通过 模块名. 使用 模块提供的工具–全局变量、函数、类

可以使用as指定模块的别名,别名要符合大驼峰命名法

import 模块名1 as 模块别名

from…import…导入

import 模块名  是 一次性 把模块中 所有工具全部导入,并且通过 模块名/别名 访问
如果希望 从某一个模块 中,导入部分 工具,就可以使用 from...import 的方式

导入之后,不需要 通过 模块名. 来访问。可以直接使用 模块提供的工具 --全局变量、函数、类

如果 两个模块,存在 同名的函数,那么 后导入模块的函数,会 覆盖掉先导入的函数

可以使用as关键字给其中一个函数起一个别名

模块的搜索顺序

搜索 当前目录 指定模块名的文件,如果有就直接导入
如果没有,再搜索 系统目录

每一个模块都有一个内置属性__file__ 可以 查看模块 的 完整路径

__name__属性可以做到,测试模块的代码 只在测试情况下被运行,而在 被导入时不会被执行!
__name__记录着一个【字符串】
    如果是被其他文件导入的, __name__ 就是 模块名;
    如果是当前执行的程序__name__ 是 __main__

【案例】

def say_hello():
    print("Hello World")

if __name__ == "__main__":
    say_hello()

包 是一个 包含多个模块 的 特殊目录
目录下有一个 特殊的文件 __init__.py 
包名的 命名方式 和变量名一致, 小写字母 + _

使用 import 包名可以一次性导入 包 中 所有的模块

案例

a.新建一个 hm_message 的包(就是一个文件夹)
b.在hm_message目录下,新建两个文件send_message.py和receive_message.py
c.在 send_message.py 文件中定义一个 send 函数
d.在 receive_message.py 文件中定义一个receive 函数
e.在 hm_message目录下新建一个 __init__.py 文件
f.在外部直接导入 hm_message 的包

【send_message.py】

def send():
    print("发送消息")

【receive_message.py】

def receive():
    print("接受消息")

【hm_message包下的init文件】

__init__.py 文件

from . import receive_message
from . import  send_message

【test_pack.py】

import hm_message

hm_message.send_message.send()
hm_message.receive_message.receive()

pip安装第三方模块

第三方模块 通常是指由 知名的第三方团队 开发的 并且被 程序员广泛使用 的 Python 包/模块。例如 pygame 就是一套非常成熟的 游戏开发模块

pip 是一个现代的,通用的Python 包管理工具,提供了对 Python 包的查找、下载、安装、卸载等功能

安装和卸载命令如下

# python2
pip install pygame
pip uninstall pygame

# python3
pip3 install pygame
pip3 unstall pygame

文件的基本操作

在 计算机 中要操作文件的套路非常固定,一共包含三个步骤
a.打开文件
b.读、写文件
  读 将文件内容读入内存
  写 将内存内容写入文件
c.关闭文件

操作文件的函数/方法

open方法:打开文件,并且返回文件操作对象
read方法:将文件内容读取到内存
write方法:将指定内容写入文件
close方法:关闭文件

open函数负责打开文件,并且返回文件对象
read/write/close三个方法都需要通过文件对象来调用

read方法

open函数的第一个参数是要打开的文件名(文件名区分大小写)
    如果文件 存在,返回 文件操作对象
    如果文件 不存在,会 抛出异常。
read 方法可以一次性 读入 并 返回 文件的 所有内容 
close方法负责 关闭文件

ps: 如果 忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问
注意:方法执行后,会把 文件指针 移动到 文件的【末尾】

【案例】

# 1.打开文件
file = open("README")

# 2.读取文件
text = file.read()
print(text)

# 3.关闭文件
file.close()

提示:在开发中,通常会先编写 打开 和 关闭 的代码,再编写中间针对文件的 读/写 操作!

文件指针(了解)

文件指针 标记 从哪个位置开始读取数据
第一次打开 文件时,通常 文件指针会指向文件的开始位置
当执行了 read 方法后,文件指针 会移动到 读取内容的末尾
    默认情况下会移动到 文件末尾

打开文件的方式

open函数默认以 只读方式 打开文件,并且返回文件对象

语法:

f = open("文件名",访问方式)

访问方式
r: 以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在抛出异常
w:以只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件
a:以追加方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入
r+:以读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常
w+:以读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件
a+:以读写方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入

提示:频繁的移动文件指针,会影响文件的读写效率,开发中更多的时候会以 只读、只写 的方式来操作文件

按行读取文件内容

read 方法默认会把文件的 所有内容 一次性读取到内存
如果文件太大,对内存的占用会非常严重

readline方法

readline 方法可以一次读取一行内容
方法执行后,会把 文件指针 移动到下一行,准备再次读取

【案例】

# 打开文件
file = open("README")
while True:
    # 读取一行内容
    line = file.readline()

    # 没有读到内容则终止循环
    if not line:
        break

    # 每读取一行的末尾以及有了一个 '\n'
    print(line, end="")
# 关闭文件
file.close()

案例-复制文件

  • 小文件复制
# 打开文件
file_read = open("README")
file_write = open("README[copy]", "w")

txt = file_read.read()
file_write.write(txt)

# 关闭文件
file_write.close()
file_read.close()
  • 大文件复制
# 打开文件
file_read = open("README")
file_write = open("README[copy-big]", "w")

while True:
    line = file_read.readline()
    if not line:
        break
    file_write.write(line)

# 关闭文件
file_write.close()
file_read.close()

文件/目录的常用管理操作

在 终端/文件浏览器中,导入os模块 可以执行常规的 文件/目录 管理操作
例如:创建、重命名、删除、改变路径、查看目录内容

文件操作

rename: 重命名文件 例如: os.rename(源文件名,目标文件名)
remove:删除文件 例如:os.remove(文件名)

目录操作

listdir:目录列表 例如:os.listdir(目录名)
mkdir:创建目录 例如: os.mkdir(目录名)
rmdir:删除目录 例如:os.rmdir(目录名)
getcwd:获取当前目录 例如:os.getcwd()
chdir:修改当前目录 例如: os.chdir(目标目录)
path.isdir:判断是否是文件 例如:os.path.isdir(文件路径)

提示:文件或者目录操作都支持 相对路径 和绝对路径

eval函数

将字符串 当成 有效的表达式 来求值 并 返回计算结果.

print(eval("1+1")) # 2

提示:不要滥用eval,不安全

------ 本文结束,感谢您的阅读 ------
本文作者: 贺刘芳
版权声明: 本文采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。