当前位置: 首页 > news >正文

Python 面向对象基础

Python 面向对象基础
Python 封装继承多态

文章目录

  • 1. 面向对象基础
    • 1.1. 面向对象概述
    • 1.2. 面向对象实现
    • 1.3. 对象和 self
  • 2. 类的成员
    • 2.1. 变量
    • 2.2. 方法
    • 2.3. 属性
      • 2.3.1. 方式一:基于装饰器
      • 2.3.2. 方式二:基于定义变量
  • 3. 成员修饰符
    • 3.1. 公有私有
    • 3.2. 单下划线、双下划线、无下划线的区别
  • 4. 特殊成员(魔法方法)
    • 4.1. \_\_init__(初始化方法)
    • 4.2. \_\_new__(构造方法)
    • 4.3. \_\_del__(析构函数)
    • 4.4. \_\_call__(对象名())
    • 4.5. \_\_str__(必须返回字符串)
    • 4.6. \_\_dict__(字典对象)
    • 4.7. \_\_getitem__、\_\_setitem__、\_\_delitem__
    • 4.8. \_\_add__ (等操作函数)
    • 4.9. \_\_enter__、\_\_exit__(上下文管理)
    • 4.10. \_\_iter__ 、\_\_next__(迭代器)
      • 4.10.1. 迭代器的定义
      • 4.10.2. 生成器(yield)
      • 4.10.3. 可迭代对象
      • 4.10.4. 实例一:基于生成器实现可迭代对象
      • 4.10.5. 实例二:基于迭代器实现可迭代对象
      • 4.10.6. 其他
  • 5. 内置函数补充

参考:
https://blog.csdn.net/2302_79546368/article/details/149090999
https://blog.csdn.net/m0_54890849/article/details/147814043?spm=1001.2101.3001.10752

1. 面向对象基础

1.1. 面向对象概述

面向对象编程是一种编程方式,此编程方式的落地需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。
类就是一个模板,模板里可以包含多个函数,函数里实现一些功能。
对象则是根据模板创建的实例,通过实例对象可以执。

1.2. 面向对象实现

面向对象实现需要 2 步:

  • 定义类,在类中定义方法,在方法中去实现具体的功能。
  • 实例化类并的个一个对象,通过对象去调用并执行方法。
'''
创建类
'''
# class 是创建类的关键字,Foo 为类名
class Foo:		# 别忘记“冒号”# 类属性name = "hello"# 类的方法:第一个参数 self 必填def detail(self):self.age = 18print(self.name)'''
创建对象
'''
foo = Foo()
foo.detail()			# 运行结果:hello
print(foo.age)			# 运行结果:18
  • 类的要素
    1、类名(要符合标识符规定,遵循大驼峰命名法,见名知义);py3 之后默认类都继承 object;
    2、属性:对象的特征描述,用来说明是什么样子的;
    3、方法:在类种编写的函数称为方法;对象具有的功能(行为),用来说明能够做什么。
#经典类
class A:      pass
#新式类
class A(object):pass    

在 python 2 中,新式类是通过继承自 object 类来定义的,而经典类类(也称为旧式类)则不继承自任何基类。
从 python 3 开始,所有的类都默认是新式类,即使它们不显式地继承自 object。不过,为了兼容性和明确性,在python 3 中仍然推荐显式地继承自 object。

1.3. 对象和 self

在每个类中都可以定义个特殊的:init 初始化方法 ,在实例化类创建对象时自动执行,即:对象=类名()。

class Foo:  #定义类# 定义类方法def __init__(self):pass
foo = Foo()

对象 = 类名() 含义:

  1. 根据类定义创建一个对象,自动执行类中的 init 方法,初始化一块内存区域。
  2. 该内存区域的地址会被赋值给 self 参数。

2. 类的成员

2.1. 变量

变量分为两类:

  • 类变量: 属于类,可以被所有对象共享,一般用于给对象提供公共数据(类似于全局变量)。
  • 实例变量: 属于对象,每个对象中各自维护自己的数据。

注意:

  • 类属性属于类,是公共的,大家都能访问到的,实例属性属于对象的,是私有的;
  • 类属性,类可以访问到,实例对象也可以访问到;实例属性只能由对象名访问,不能由类名访问。
class Foo:# 类属性name = "hello"def __init__(self):# 实例属性self.age = 18passfoo = Foo()
foo.name = "world"
print(Foo.name)     # hello
print(foo.name)     # world# 实例属性不能通过类名访问
print(Foo.age)      # AttributeError: type object 'Foo' has no attribute 'age'
print(foo.age)      # 18

2.2. 方法

  • 绑定方法: 默认有一个 self 参数,由对象进行调用(此时 self 就等于调用方法的这个对象)【对象&类均可调用】
  • 类方法: 默认有一个 cls 参数,用类或对象都可以调用(此时 cls 就等于调用方法的这个类)【对象&类均可调用】
  • 静态方法: 无默认参数,用类和对象都可以调用。【对象&类均可调用】
class Foo(object):def __init__(self, name, age):#初始化方法self.name = nameself.age = age# 绑定方法def f1(self):print("绑定方法", self.name)# 类方法@classmethoddef f2(cls):print("类方法", cls)# 静态方法@staticmethoddef f3():print("静态方法")# 绑定方法(对象)
obj = Foo("张三", 20)
obj.f1()  
Foo.f1(obj)# 类方法
Foo.f2()  # cls就是当前调用这个方法的类。(类)
obj.f2()  # cls就是当前调用这个方法的对象的类。# 静态方法
Foo.f3()  # 类执行执行方法(类)
obj.f3()  # 对象执行执行方法

运行结果:

绑定方法 张三
绑定方法 张三
类方法 <class '__main__.Foo'>
类方法 <class '__main__.Foo'>
静态方法
静态方法

2.3. 属性

属性其实就是由 绑定方法 + 特殊装饰器 组合创造出来的,属性的出现让我们以后在调用方法时可以不加括号,例如:

class Foo(object):def __init__(self, name):self.name = namedef f1(self):print("绑定方法", self.name)@propertydef f2(self):print("属性方法", self.name)obj = Foo("李四")
v1 = obj.f1()v2 = obj.f2     # v2 = obj.f2()

运行结果:

绑定方法 李四
属性方法 李四

关于属性的编写有两种方式:

2.3.1. 方式一:基于装饰器

class Foo(object):def __init__(self):self.value=0@propertydef x(self):return self.value@x.setterdef x(self, value):self.value = value@x.deleterdef x(self):self.value=0obj = Foo()
# obj.x
obj.x = 123
print(obj.x)  # 123
del obj.x
print(obj.x)  # 0

2.3.2. 方式二:基于定义变量

class Foo(object):def __init__(self):self.value=0def getx(self):return self.valuedef setx(self, value):self.value = valuedef delx(self):self.value=0x = property(getx, setx, delx, doc="I'm the 'x' property.")obj = Foo()
# obj.x
obj.x = 123
print(obj.x)  # 123
del obj.x
print(obj.x)  # 0

注意: 由于属性和实例变量的调用方式相同,所以在编写时 属性名称 不要 实例变量 重名。一旦重名,可能就会有报错。如果真的想要在名称上创建一些关系,可以让实例变量加上一个下划线。

3. 成员修饰符

3.1. 公有私有

python 中成员的修饰符就是指的是:公有、私有。

  • 公有: 在任何地方都可以调用这个成员。
  • 私有: 只有在类的内部才可以调用该成员(成员是以两个下划线开头,则表示该成员为私有)。
class Foo(object):def __init__(self, name, age):self.__name = nameself.age = agedef get_name(self):return self.__namedef get_age(self):return self.agedef get_func(self):print("公有方法 get_func")def __get_func(self):print("私有方法 __ get_func")def proxy(self):self.__get_func()print("公有的 proxy")'''
变量访问
'''
obj = Foo("王五", 123)
# 公有变量
print(obj.age)          # 123
# 私有成员
# print(obj.__name)       # AttributeError: 'Foo' object has no attribute '__name',由于是私有成员,只能在类中进行使用。# 私有变量,可以通过方法访问
v2 = obj.get_name()
print(v2)               # 王五'''
方法访问
'''
o1 = Foo("赵六",10)
# 公有方法
o1.get_func()           # 公有方法 get_func
# 私有方法
# o1.__get_func()         # AttributeError: 'Foo' object has no attribute '__get_func'. Did you mean: 'get_func'? 私有方法只能在类内部进行访问# 私有方法,可以通过共有方法访问
o1.proxy()              # 私有方法 __ get_func \n 公有的 proxy

注意: 父类中的私有成员,子类无法继承。

按理说私有成员是无法被外部调用,但如果用一些特殊的语法也可以(Flask 源码中有这种写法,大家写代码不推荐这样写【_类名__变量名/方法名/属性名】)。

class Foo(object):def __init__(self):self.__num = 123self.age = 19def __msg(self):print(1234)obj = Foo()
# 私有变量
print(obj.age)          # 19
# print(obj.__num())     # 会报错
print(obj._Foo__num)    # 123
# 私有方法
# print(obj.__msg())    # 会报错
obj._Foo__msg()         # 1234

3.2. 单下划线、双下划线、无下划线的区别

  • 无下划线属性
    普通属性/方法,完全公开,可被外部直接访问或修改。
  • 双下划线开头
    私有属性/方法,外部不能直接访问,需要通过名称修饰机制来访问;
    子类不能直接继承父类的私有属性和方法,因为名称修饰机制会改变属性和方法的名称。
    在另一个 py 文件中通过 from xxx import * 导入的时候,可以导入,因为 python 的 all 列表默认不会排除双下划线开头的成员;
    魔法方法和属性是双下划线开头且双下划线结尾,而双下划线开头但不双下划线结尾的是私有成员。
  • 单下划线开头
    隐藏属性,如果定义在类中,外部可以直接访问,但不建议这么做;
    子类可以继承父类单下划线开头的属性/方法。
    另一个 py 文件中通过 from xxx import * 导入的时候,默认不会导入;主要是作为一种约定,提示开发者这是类内部使用的成员。

4. 特殊成员(魔法方法)

在 python 的类中存在一些特殊的方法,这些方法都是 方法 格式,这种方法在内部均有特殊的含义,也称魔法方法。

4.1. __init__(初始化方法)

python 内置方法,通常用来做属性初始化或者赋值操作。
注意:在类实例化对象的时候,会自动调用。

class Foo(object):def __init__(self, name):self.name = nameobj = Foo("张三")

4.2. __new__(构造方法)

class Foo(object):def __init__(self, name):print("第二步:初始化对象,在空对象中创建数据")self.name = namedef __new__(cls, *args, **kwargs):print("第一步:先创建空对象并返回")return object.__new__(cls)obj = Foo("张三")

4.3. __del__(析构函数)

__del__() 主要是表示该程序块或者函数已经全部执行结束,删除对象的时候,解释器会默认调用 __del__() 方法。

class Foo:def __init__(self):print('我是__init__()')def __del__(self):print('我是__del__()')obj = Foo()

运行结果:

我是__init__()
我是__del__()

执行 del 的时候,内存会立即被回收,也会调用对象本身的 __del__()方法。

4.4. __call__(对象名())

class Foo(object):def __call__(self, *args, **kwargs):return "执行 call 方法"obj = Foo()
# 当遇见 对象名() 的时候就会自动执行 call 方法 
print(obj())    # 执行 call 方法

4.5. __str__(必须返回字符串)

class Foo(object):def __str__(self):return "必须返回字符串"obj = Foo()
# 下面两个语句打印的结果是一样的,第一个是对第二个语句内部做了优化而已
print(obj)		# 必须返回字符串
print(str(obj))	# 必须返回字符串

4.6. __dict__(字典对象)

字典对象的支持:

对象[""]
对象[]=xxxx
del 对象[]

可以和下面的三种方法搭配使用

class Foo(object):def __init__(self, name, age):self.name = nameself.age = ageobj = Foo("张三", 19)
print(obj.__dict__)     # {'name': '张三', 'age': 19}
print(obj.__dict__["name"]) # 张三

4.7. __getitem__、__setitem__、__delitem__

class Foo(object):def __setitem__(self, key, value):self.value = value# print(key, value)def __getitem__(self, value):return self.valuedef __delitem__(self,key):self.value = Nonereturn keyobj = Foo()
obj["test"]=19
print(obj.__dict__)         # {'value': 19}
print(obj["test"])          # 19
del obj["test"]
print(obj["test"])          # None

4.8. __add__ (等操作函数)

class Foo(object):def __init__(self, name):self.name = namedef __add__(self, other):return "{}-{}".format(self.name, other.name)v1 = Foo("I")
v2 = Foo("Love")
# 对象+值,内部会去执行 对象.__add__方法,并将+后面的值当做参数传递过去。
v3 = v1 + v2
print(v3)           # I-Love
v4 = Foo("You")v5= Foo(v3) + v4
print(v5)           # I-Love-You

4.9. __enter__、__exit__(上下文管理)

class Foo(object):def __enter__(self):print("进入了")return 666def __exit__(self, exc_type, exc_val, exc_tb):print("出去了")obj = Foo()
with obj as data:print(data)

运行结果:

进入了
666
出去了

4.10. __iter__ 、__next__(迭代器)

4.10.1. 迭代器的定义

当类中定义了 __iter__ 和 __next__ 两个方法;
__iter__ 方法需要返回对象本身,即:self;
__next__ 方法,返回下一个数据,如果没有数据了,则需要抛出一个 StopIteration 的异常。

class Foo(object):def __init__(self):self.counter = -1def __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == 2:raise StopIteration()return self.counter# 根据类实例化创建一个迭代器对象:
obj1 = Foo()
v1 = next(obj1)     # obj1.__next__()
print(v1)           # 0
v2 = next(obj1)
print(v2)           # 1
# v3 = next(obj1)     # 抛出异常,异常会在后面的内容中讲到
# print(v3)print("############################")
obj2 = Foo()
for item in obj2:   # 首先会执行迭代器对象的__iter__方法并获取返回值,一直去反复的执行 next(对象) print(item)     # 0 \n 1  

迭代器操作流程:

  1. 迭代器对象支持通过 next 取值;for 循环内部在循环时,先执行 iter 方法,获取一个迭代器对象;
  2. 然后不断执行的 next 取值(有异常 StopIteration 则终止循环);
  3. 如果取值结束则自动抛出 StopIteration。

4.10.2. 生成器(yield)

创建生成器对象(内部是根据生成器类 generator 创建的对象),生成器类的内部也声明了:__iter__、__next__ 方法。如果按照迭代器的规定来看,其实生成器类也是一种特殊的迭代器类(生成器也是一个中特殊的迭代器)。

# 生成器
def func():yield 0yield 1obj2 = func()
for item in obj2:print(item)     # 0 \n 1

4.10.3. 可迭代对象

如果一个类中有 __iter__ 方法且返回一个迭代器对象 ,则我们称这个类创建的对象为可迭代对象
可迭代对象,可以使用 for 来进行循环;
在循环的内部其实是先执行 __iter__ 方法,获取其迭代器对象,然后再在内部执行这个迭代器对象的 next 功能,逐步取值。

class Foo(object):def __iter__(self):return "迭代器对象(生成器对象)"obj = Foo()       # obj 是 可迭代对象。
for item in obj:pass

4.10.4. 实例一:基于生成器实现可迭代对象

# 可迭代对象
class Foo1(object):def __iter__(self):# 生成器yield 0yield 1
# 上下等效
# class Foo1(object):
#     def __iter__(self):
#         counter = 0
#         while counter < 2:
#             yield counter
#             counter += 1obj1 = Foo1()
for item in obj1:print(item)     # 0 \n 1

4.10.5. 实例二:基于迭代器实现可迭代对象

# 迭代器
class IT(object):def __init__(self):self.counter = -1def __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == 2:raise StopIteration()return self.counter     # 可迭代对象# 可迭代对象
class  Foo2(object):def __iter__(self):return IT()obj2 = Foo2()         
for item in obj2:    # 循环可迭代对象时,内部先执行 obj.__iter__ 并获取迭代器对象;不断地执行迭代器对象的 next 方法。print(item)		# 0 \n 1

4.10.6. 其他

Iterator 检查对象是否是迭代器;
Iterable 检查对象是否可以被迭代;
所有的迭代器都是可迭代的,但并非所有的可迭代对象都是迭代器。

from collections.abc import Iterator, Iterable
v1 = [11, 22, 33]
print( isinstance(v1, Iterator) )  # false,判断是否是迭代器;判断依据是__iter__ 和 __next__。
v2 = v1.__iter__()
print( isinstance(v2, Iterator) )  # True
v1 = [11, 22, 33]
print( isinstance(v1, Iterable) )  # True,判断依据是是否有 __iter__且返回迭代器对象。
v2 = v1.__iter__()
print( isinstance(v2, Iterable) )  # True,判断依据是是否有 __iter__且返回迭代器对象。

5. 内置函数补充

本次要给讲解的内置函数共8个,他们都跟面向对象的知识相关。
classmethod:
staticmethod:
property:
callable:是否可在后面加括号执行。所以当你以后在见到下面的情况时,首先就要想到 handler 可以是:函数、类、具有 call 方法的对象这三种,到底具体是什么,需要根据代码的调用关系才能分析出来。
super:按照 mro 继承关系向上找成员。
type:获取一个对象的类型。
isinstance:判断对象是否是某个类或其子类的实例。
issubclass:判断类是否是某个类的子孙类。

http://www.xdnf.cn/news/1189693.html

相关文章:

  • 力扣刷题(第九十九天)
  • Rust嵌入式开发实战
  • 去除视频字幕 4 : 下一步,打算研究 Video Inpainting (视频修复):
  • Redis 缓存机制详解:原理、问题与最佳实践
  • Effective C++ 条款4:确定对象被使用前已先被初始化
  • 编程与数学 03-002 计算机网络 06_网络层职责
  • 设计模式十一:享元模式(Flyweight Pattern)
  • 路由选择工具——IP-Prefix
  • 如何查看电脑后门IP和流量?
  • 变频器实习DAY15
  • Kafka MQ 消费者应用场景
  • 机器人仿真(2)Ubuntu24.04下RTX5090配置IsaacSim与IsaacLab
  • 推荐系统(第三课第二周)
  • 【AcWing 143题解】最大异或对
  • Item14:在资源管理类中小心拷贝行为
  • 高并发微服务限流算法方案对比与实践指南
  • xLua和C#交互
  • 激光雷达-相机标定工具:支持普通相机和鱼眼相机的交互式标定
  • 字节跳动扣子 Coze 宣布开源:采用 Apache 2.0 许可证,支持商用
  • 6.数组和字符串
  • J2EE模式---表现层集成模式
  • 备份一下我的 mac mini 的环境变量配置情况
  • net-snmp添加自定义mib树
  • 【C++基础】指针常量 | 常量指针 | int* p | const int* p | int* const p| const int* const p
  • 详解力扣高频SQL50题之619. 只出现一次的最大数字【简单】
  • PCIe 的L状态(链路状态)和D状态(设备状态)
  • 前端组件梳理
  • 【WPF】NumericUpDown的用法
  • 【CTF-WEB-反序列化】利用__toString魔术方法读取flag.php
  • 教育培训系统源码解析:如何打造高可扩展的在线学习平台?