继承

继承的语法

class Animal(object):
def __init__(self,name):
self.__name = name class Dog(Animal):
kind = "Dog"
def __init__(self,name,age):
super.__init__(self,name)
self.__age = age

可以看出,定义类时后面圆括号里的就是父类(基类),如果有多个父类或者metaclass时,用逗号隔开。只要子类成员有与父类成员同名,默认覆盖父类成员的,类似Java中的方法重写(@override)。

super详解

说到 super(), 大家可能觉得很简单呀,不就是用来调用父类方法的嘛。如果真的这么简单的话也就不会单独拿出来说了

单继承

在单继承中super()就像大家所想的那样,主要是用来调用父类的方法的

class A:
def __init__(self):
self.n = 2 def add(self, m):
print(f'self is {self} @A.add')
self.n += m def __str__(self):
return "instance of A" class B(A):
def __init__(self):
self.n = 3 def __str__(self):
return "instance of B" def add(self, m):
print(f'self is {self} @B.add')
super().add(m)
self.n += 3

你觉得执行下面代码后, b.n 的值是多少呢?

b = B()
b.add(2)
print(b.n)

执行结果如下:

self is instance of B @B.add

self is instance of B @A.add

8

这个结果说明了两个问题:

  • super().add(m) 确实调用了父类 A 的 add 方法
  • super().add(m) 调用父类方法 def add(self, m) 时, 此时父类中 self 不是父类的实例而是子类的实例, 所以 b.add(2) 之后的结果是 5 而不是4

多继承

这次我们再定义一个 class C,一个 class D

class C(A):
def __init__(self):
self.n = 4 def __str__(self):
return "instance of C" def add(self, m):
print(f'self is {self} @C.add')
super().add(m)
self.n += 4 class D(B, C):
def __init__(self):
self.n = 5 def __str__(self):
return "instance of D" def add(self, m):
print(f'self is {self} @D.add')
super().add(m)
self.n += 5

下面的代码又输出啥呢?

d = D()
d.add(2)
print(d.n)

这次的输出如下:

self is instance of D @D.add

self is instance of D @B.add

self is instance of D @C.add

self is instance of D @A.add

19

为什么是这么样的顺序?继续往下看!

super是个类

当我们调用super()的时候,实际上是实例化了一个super类。你没看错, super 是个类,既不是关键字也不是函数等其他数据结构

>>> class A:
pass >>> s = super(A)
>>> type(s)
<class 'super'>

在大多数情况下, super 包含了两个非常重要的信息: 一个 MRO 以及 MRO 中的一个类

  • 当以如下方式调用 super 时:

    super(a_type, obj)并且isinstance(obj, a_type) = True

    MRO 指的是 type(obj)MRO, MRO 中的那个类就是 a_type
  • 当这样调用时:

    super(type1, type2)并且issubclass(type2, type1) = True

    MRO 指的是 type2MRO, MRO 中的那个类就是 type1

那么,super(arg1, arg2).func()实际上做了啥呢?简单来说就是:

根据arg2得到MRO列表,然后,查找arg1MRO列表的位置,假设为pos,然后从pos+1开始查找,找到第一个有func()的类,执行该类的func()

虽然super无返回值,但可以认为,它返回值类型和arg2同类型

MRO(Method Resolution Order)——方法解析顺序

可以通过类名.mro()方法查找出来当前类的调用顺序,其顺序由C3线性算法来决定,保证每一个类只调用一次,下面举个例子就能看懂了:

MRO的C3算法计算过程——计算G的MRO列表

C3算法核心操作是merge,也就是按照类似拓扑排序的方法,把多组mro列表合并。

为简单表示,将object写成O

C3线性算法的实现

import abc
def mro(cls) -> list:
if type(cls) == type(object) or type(cls) == type(abc.ABC):
# object是最顶层类,如果传入的是object,那么就是[object]
if issubclass(object,cls):
return [cls]
else:
bases = cls.__bases__
return __merge(cls, *[mro(x) for x in bases], list(bases))
else:
raise TypeError("mro()方法需要一个位置参数cls,类型为class") def __merge(*args)->list:
result = [args[0]]
operation_list = list(args[1:])
"""
any()函数:
Return True if bool(x) is True for any x in the iterable.
If the iterable is empty, return False.
"""
while any(operation_list):
# 将空的列表删除,因为有可能传进来的是[[],[],['object']]
while [] in operation_list:
operation_list.remove([]) # 拓扑序列中的每个元素(类名列表)
for y in operation_list:
temp = y[0]
need = True
for t in operation_list:
if temp in t and t.index(temp) > 0:
need = False
break
if need:
break; # 将这个元素添加到结果列表
result.append(temp)
# 拓扑排序的列表中删除这个元素节点
for p in operation_list:
while temp in p:
p.remove(temp)
else:
return result

抽象类

与java一样,python也有抽象类的概念,但是同样需要借助模块实现。

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。如果一个抽象基类要求实现指定的方法,而子类没有实现的话,当试图创建子类或者执行子类代码时会抛出异常。

Python中,定义抽象类需要借助abc模块,abc模块提供了一个使用某个抽象基类声明协议的机制,并且子类一定要提供了一个符合该协议的实现。

from abc import ABC
import abc
# class Animal(metaclass=abc.ABCMeta):
class Animal(ABC):
@abc.abstractmethod
def test(cls):
pass @abc.abstractmethod
def bark(self):
pass class Dog(Animal):
# 子类需要实现抽象方法,否则报错
def bark(self):
print("汪汪汪...") @classmethod
def test(cls):
print("Dog的test") class Cat(Animal):
def bark(self):
print("喵喵喵...") def test(self):
print("Cat的test") # a = Animal() # 抽象类不能实例化
d = Dog()
d.bark()
Dog.test() c = Cat()
c.bark()
c.test()
  1. 必须导入abc模块
  2. 抽象方法用装饰器 @abc.abstractmethod,子类实现时才能确认是实例方法、类方法还是静态方法
  3. 抽象类,要么继承ABC类;要么不写父类,写metaclass=ABCMeta,由元类ABCMeta创建

枚举类

例如星期、月份这些类型,它们的值是公认的,不会随意更改,所以可以事先将这些值都定义出来,用的时候直接拿过来用,这就是枚举类和枚举类型,最主要的一点是穷尽,枚举类型必须是一个枚举类的所有可能结果。这些枚举类型都是只创建一次对象的,每个人要用都是用一样的对象,是不可变的。

from enum import Enum, unique

# 创建月份的枚举类
# 枚举类中的每个属性值,默认是从1开始递增的整数
# 如果指定了一个属性的值,它后面的属性则会从该值开始递增
# PS:下面这个就是个构造方法,Enum就是个类
Month = Enum('Month',('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) # 如果想自定义属性的值可以这么写,继承Enum类
# unique装饰器可以帮助我们检查,保证没有重复值
@unique
class WeekDay(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6 # 访问枚举类型的值
print(WeekDay.Mon) # WeekDay.Mon
print(WeekDay['Tue']) # WeekDay.Tue
print(WeekDay.Wed.name) # Wed
print(WeekDay.Wed.value) # 3
print(WeekDay(4)) # WeekDay.Thu

反射

在程序开发中,常常会遇到这样的需求:在执行对象中的某个方法,或者在调用对象的某个变量,但是由于一些原因,我们无法确定或者并不知道该方法或者变量是否存在,这时我们需要一个特殊的方法或者机制来访问或操作该未知的方法或变量,这种机制就被称之为反射。

反射机制:反射就是通过字符串的形式,导入模块;通过字符串的形式,去模块中寻找指定函数,对其进行操作。也就是利用字符串的形式去对象(模块)中操作(查找or获取or删除or添加)成员,一种基于字符串的事件驱动。

下面介绍反射机制的四个方法:

  • hasattr()函数

    语法:hasattr(object, name)

    功能:判断object中是否有属性或者方法name,其中object可以是对象、可以是类、可以是模块名,存在返回True,不存在返回False。
  • getattr()函数

    语法:getattr(object, name, default=None)

    功能:返回object的name属性(或方法),不存在则看default有没有传,没有就会报错,传了的话返回default值作为属性(或方法)不存在的返回值。
  • setattr()函数

    语法:setattr(object,name,value)

    功能:动态给属性赋值,如果属性不存在,则先创建属性再赋值,另外,这是运行时修改,不会影响文件代码的内容。
  • delattr()函数

    语法:delattr(object,name)

    功能:删除object的name属性,,属性不存在则报错。
import functools

class Student(object):
std_no = 1001
def __init__(self,name):
self.name =name
def f(self,string):
print(string) s = Student("芜情")
print(hasattr(functools,"reduce")) # True
print(hasattr(Student,"std_no")) # True
print(hasattr(Student,"f")) # True
print(hasattr(s,"name")) # True print(getattr(Student,"name","None")) # None
getattr(Student,"f","None")(s,"WCG") # WCG setattr(Student,"sex","male")
print(getattr(s,"sex","None")) # male
setattr(Student,"f",functools.reduce)
print(getattr(s,"f")) # <built-in function reduce> setattr(Student,"sex","male")
print(getattr(s,"sex","None")) # male
setattr(Student,"f",functools.reduce)
print(getattr(s,"f")) # <built-in function reduce> delattr(Student,"std_no")
print(getattr(Student,"std_no","None")) # None
delattr(Student,"f")
print(getattr(Student,"f","None")) # None

dataclass数据类

dataclass装饰器

语法:

dataclass(*, init = True, repr = True, eq = True, order = False, unsafe_hash = False, frozen = False)

参数含义

  • init:默认True,则自动生成__init__()方法
  • repr:默认True,则自动生成__repr__()方法,格式为类名和各参数名以及参数值
  • eq:默认True,自动生成__eq__()方法,此方法按顺序比较属性的元组
  • order:默认False,如果为True,则自动生成__lt__()、__le__()、__g__t()、__ge__()方法
  • unsafe_hash:暂且不管,和hash值有关,一般遇不到
  • frozen:默认False,如果为True,则禁止更改属性值(类似Java中的final)

示例

首先,要知道下面三种写法是等价的

@dataclass
@dataclass()
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)

下面给出具体例子

from dataclass import dataclass
@dataclass(order=True)
class Student(object):
name:str
age:int = 18 s = Student("yee",16)
t = Student("sky")
print(s) # Student(name='yee', age=16)
print(s==t) # False
print(s>t) # True
t.name = "疾风剑豪" # frozen=True时,此方法报错
print(t) # Student(name='疾风剑豪', age=18)

__post_init__

有些操作需要在初始化后进行,如分离浮点数的整数部分和小数部分:

from dataclasses import dataclass,field
import math
@dataclass
class FloatNumber:
val: float = 0.0
def __post_init__(self): # 方法签名固定,不能改
self.decimal, self.integer = math.modf(self.val) a = FloatNumber(2.2)
print(a) # FloatNumber(val=2.2)
print(a.val) # 2.2
print(a.integer) # 2.0
print(a.decimal) # 0.20000000000000018

field函数

语法:

field(*, default=MISSING, default_factory=MISSING,
init=True,repr=True,hash=None,
compare=True,metadata=None):

MISSING代表类体只有pass语句的类

参数含义

  • default:如果提供,这将是此字段的默认值。这是必需的,因为field()调用本身取代了默认值的正常位置
  • default_factory:如果提供,它必须是零参数可调用,当此字段需要默认值时将调用该调用,default_factory不能和default同时出现
  • init:如果为true(默认值),则此字段作为参数包含在生成的__init__()方法中
  • repr:如果为true(默认值),则此字段包含在生成的__repr__()方法返回的字符串中
  • hash:一般不用
  • compare:如果为真(默认值),则该字段被包括在所产生的==和比较方法
  • metadata:这是给第三方的API,我们用不到

示例

from dataclasses import dataclass,field

@dataclass
class Student(object):
Chinese:float
Maths:float
English:float
total:float = field(init=False)
def __post_init__(self):
self.total = self.Chinese + self.Maths + self.English s = Student(98,99,95)
print(s) # Student(Chinese=98, Maths=99, English=95, total=292)

继承问题

这里继承和一般的继承一样,子类会继承父类的属性和方法,按照MRO列表的顺序,查找所调用的函数和属性,都找不到则报错。

『Python』面向对象(二)的更多相关文章

  1. 『Python』面向对象(一)

    类和对象 类(class)是用来描述具有相同属性(attribute)和方法(method)的对象的集合,对象(object)是类(class)的具体实例.比如学生都有名字和分数,他们有着共同的属性. ...

  2. 『Python』__getattr__()特殊方法

    self的认识 & __getattr__()特殊方法 将字典调用方式改为通过属性查询的一个小class, class Dict(dict): def __init__(self, **kw) ...

  3. 第八章:Python基础の面向对象(二)

    本課主題 面向对象的多态 面向对象的成员 成员修饰符 特殊成员 面向对象其他应用 异常处理 设计模式与单例模式 面向对象的多态 指定参数类型只是多态的一种表现 另外一种是允许自己类型和自己的子类型(典 ...

  4. Python之面向对象二

    面向对象的三大特性: 继承 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类 python中类的继承分为:单继承和多继承 c ...

  5. 『Python』为什么调用函数会令引用计数+2

    一.问题描述 Python中的垃圾回收是以引用计数为主,分代收集为辅,引用计数的缺陷是循环引用的问题.在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存. sys.g ...

  6. 『Python』装饰器

    一.参考 作者:zhijun liu 链接:https://www.zhihu.com/question/26930016/answer/99243411 来源:知乎 建议大家去原答案浏览 二.装饰器 ...

  7. 『Python』内存分析_list和array

    零.预备知识 在Python中,列表是一个动态的指针数组,而array模块所提供的array对象则是保存相同类型的数值的动态数组.由于array直接保存值,因此它所使用的内存比列表少.列表和array ...

  8. 『Python』VS2015编译源码注意事项

    一.2.5.6版本源码编译 解压 Python-2.5.6.tgz 进入 Pcbuild8 文件夹,使用 vs 2013 打开 pybuild.sln (vs 解决方案),进入 vs2015IDE 环 ...

  9. 『Python』源码解析_源码文件介绍

    本篇代码针对2.X版本,与3.X版本细节不尽相同,由于两者架构差别不大加之本人能力有限,所以就使用2.X体验python的底层原理了. 一.主要文件夹内容 Include :该目录下包含了Python ...

随机推荐

  1. luogu P4206 聪聪和可可

    聪聪和可可 鸽了两天 \(dijkstra\)预处理出来两点之间的最短路径\(dis\)数组,读题发现,\(cat\)的走位很怪sb斩了,所以我们设一个\(next\)数组,\(next[i][j]\ ...

  2. C语言预处理编译链接各个阶段错误,分阶段的说一下

    C语言预处理编译链接各个阶段错误,分阶段的说一下 C语言预处理编译链接各个阶段错误,分阶段的说一下比如指针异常,数组下标越界什么的    我来答 1个回答 #热议# 你觉得这辈子有希望看到996消失 ...

  3. C# 高级进阶(一)

    产品是怎样开发出来的 首先了解--产品MVP(Minimum Viable Product)的概念 最小可行产品--意思即开发最初应该是根据参品预期,最快构建出一个符合预期可行的功能合集,后期再通过用 ...

  4. Spring Boot Mybatis注解:@Mapper和@MapperScan

    使用@Mapper注解 添加了@Mapper注解之后这个接口在编译时会生成相应的实现类,让其他的类进行引用 @Mapper public interface EmpMapper { public Li ...

  5. MVVMLight学习笔记(七)---Messenger使用

    一.概述 Messenger中文解释为信使的意思,顾名思义,在MvvmLight中,它的主要作用是用于View和ViewModel.ViewModel和ViewModel之间的通信. 考虑以下场景: ...

  6. go测试--进阶

    目录 前言 控制编译的参数 -args -json -o 控制测试的参数 -bench regexp -benchtime s -cpu 1,2,4 -count n -failfast -list ...

  7. spring支持的Bean的作用域

    Sigleton:单例模式,在整个Spring IoC容器中,使用Sigleton定义Bean将有一个实例 prototype:原型模式,每次通过容器的getBean方法获取propertype都将产 ...

  8. 四种cmd打开方式

    四种cmd打开方式: 开始+系统+命令提示符: Win+R 输入cmd回车(推荐使用): 按住shift键加右键,点击打开PowerShell窗口: 资源管理器的地址栏前加入cmd+空格

  9. 【CSS】模仿迅雷主页的按钮

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. Jetpack Compose学习(3)——图标(Icon) 按钮(Button) 输入框(TextField) 的使用

    原文地址: Jetpack Compose学习(3)--图标(Icon) 按钮(Button) 输入框(TextField) 的使用 | Stars-One的杂货小窝 本篇分别对常用的组件:图标(Ic ...