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()
对象 = 类名() 含义:
- 根据类定义创建一个对象,自动执行类中的 init 方法,初始化一块内存区域。
- 该内存区域的地址会被赋值给 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
迭代器操作流程:
- 迭代器对象支持通过 next 取值;for 循环内部在循环时,先执行 iter 方法,获取一个迭代器对象;
- 然后不断执行的 next 取值(有异常 StopIteration 则终止循环);
- 如果取值结束则自动抛出 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:判断类是否是某个类的子孙类。