Python学习笔记(2)

抽象类:

抽象类是包含抽象方法的类,该类不能被实例化,而抽象方法不包含任何可实现的代码(常用pass语句),只能在其子类中实现抽象函数的代码(一般被子类继承,通过子类实例化后重写方法。

子类一定要实现重写抽象类的所有抽象方法,否则这个子类就是一个抽象类,不能被实例化。

python 本身中不存在抽象类、接口的概念,要实现这种功能需要abc.py这个类库。具体方式:在定义抽象类前需要从类库abc 导入 ABCmeta类(即Metaclass for defining ABstract BaseClasses, 抽象基类的元类)和abstractmethod类。

定义抽象类的步骤:

1、在定义抽象类时需要在类定义中加入如下代码: metaclass=abc.ABCMeta ,  即指定该类的元类是 ABCmeta , 所谓元类就是创建类的类。

2、在定义抽象方法时需要在前面加入如下代码:  @abstractmethod   ;  因为抽象方法不包含任何可实现的代码,因此函数体通常使用 pass 。

复制代码
import abc
from abc import abstractmethod

class Test(metaclass=abc.ABCMeta):
  def __init__(self):
    print('做点什么')

  @abstractmethod
  def do(self):
    pass
复制代码

 

接口:

接口是一种特殊的类。当抽象类中所有的方法都是抽象方法时,或者一个普通类所有的方法都没有实现逻辑,我们可以把这个类当成一个接口 Interface。

Python中并没有 Interface 这个关键字来定义一个接口,其实就是定义一个抽象类,个人可理解为 是为了规范子类中的所有方法一致性。

接口有两点要注意:

1、接口的实现类(子类)必须要有接口中所有的方法

2、接口的实现类(子类)实现的方法必须跟接口中所有的方法的名字一样

接口有两种实现方式:

复制代码
import abc
from abc import abstractmethod

#使用抽象类实现,该类里面全是抽象方法
class Interface1(metaclass=abc.ABCMeta):
  @abstractmethod
  def show(self):
    pass

# 使用普通类实现,该类里面全是没有实现逻辑的方法
class Interface2:
  def show(self):
    pass

class Test(Interface1):
  def show(self):
    print('重写每一个方法,赋予逻辑')
    
class Test2(Interface2):
  def show(self):
    print('重写每一个方法,赋予逻辑')
复制代码

 

多态:

所谓多态指的是程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才决定。

简单理解:  一个函数的参数,通过传入参数的不同具备不同的意义;或者 + 号在不同的地方使用具备不同的意义,这就是多态。

多态的好处: OOP的开闭合原则,对添加代码开放,对修改代码闭合。

实现多态的方法: 1、使用继承;  2、使用接口

 

重载:

重新定义print输出的内容,使用__str__() 方法,该方法 return 回一个 输出的值。 在类里面定义该方法,直接输出实例对象,会输出 __str__() 方法返回的值

class Test:
  def __str__(self):
    return '这是重新定义输出的内容'

test = Test()

print(test)    # 这是重新定义输出的内容

运算符重载,就是对已有的运算符重新进行定义,赋予其另一种新的功能,以适应不同的数据类型。在一个类中具备了对运算符进行重载的多个方法,该方法可以重新定义两个实例对象相加或相减等运算的结果。

复制代码
# encoding=utf-8

class Vector:
  def __init__(self, x, y):
    self.X = x
    self.Y = y
  # 重载print()方法
  def __str__(self):
    return "[{0}, {1}]".format(self.X, self.Y)
  # 重载 + 运算符
  def __add__(self,other):
    return Vector(self.X + other.X, self.Y + other.Y)
  # 重载 - 运算符
  def __sub__(self,other):
    return Vector(self.X - other.X, self.Y - other.Y)
  # 重载 * 运算符
  def __mul__(self,other):
    return Vector(self.X * other.X, self.Y * other.Y)
  # 重载 % 运算符
  def __mod__(self,other):
    return Vector(self.X % other.X, self.Y % other.Y)


v1 = Vector(5, 6)
print(v1)   # [5, 6]

v2 = Vector(5, 6)
v3 = Vector(3, 3)
print(v2 + v3)    # [8, 9]

v4 = Vector(5, 6)
v5 = Vector(3, 3)
print(v4 - v5)    # [2, 3]

v6 = Vector(5, 6)
v7 = Vector(3, 3)
print(v6 * v7)    # [15, 18]

v8 = Vector(5, 6)
v9 = Vector(3, 3)
print(v8 % v9)    # [2, 0]
复制代码

自定义异常: 除了默认的异常类型,还可以使用 repr() 方法 在类里面自定义异常:

复制代码
#encoding=utf-8

class MyException(Exception):
  def __init__(self, value):
    self.value = value
  def __str__(self):
    return repr(self.value)    # 自定义抛出异常信息

try:
  test = int(input('请输入年龄'))
  if test > 130:
    raise MyException('年龄不能大于130')   # 自定义抛出异常
  if test < 1:
    raise MyException('年龄不能小于1')     # 自定义抛出异常
except ValueError as e:
  print('请输入数字')
except MyException as a:   # 使用自定义的异常类型
  print(a.value)
复制代码

 

迭代器: 使用 iter()  方法, 使某个集合转换为迭代对象, 使用next() 依次输出里面的值。通常跟for 循环配合使用;

list1 = [1,2,3,4,5,6,7,8]
iter1 = iter(list1)

print(next(iter1))  # 1
print(next(iter1))  # 2

for t in iter1:
  print(t)

 

列表生成器: 通过列表生成式,我们可以直接创建一个列表。但是受到内存限制,列表容量肯定是有限的。而且当创建的列表包含100万个数据时,会占用很大的内存,如果我们仅仅使用到前几个数据,那么后面元素占用的空间就浪费了。所以,将列表元素按照某种算法推算出来,不必创建完整的list, 从而节省大量的空间,这种一边循环一边计算的机制,称为生成器(Generator) 。

创建生成器的方法: 把一个列表生成式的 []  改为 ()

list1 = [x for x in range(1,1000)]    #  列表生成式
list2 = (x for x in range(1,1000))    #  列表生成器
print(list2)

 

创建函数生成器的方法:在一个函数内定一个 yield 语句,那个这个函数就变成了 Generator 。 函数生成器和函数的执行流程不一样,函数是顺序执行,遇到return或者执行完毕就返回。而变成generator的函数,在每次调用next() 的时候执行,遇到 yield 语句返回,再次执行时从上次返回的yield语句处继续执行。   如果在 yield 语句前先遇到return ,会直接停止迭代并抛出异常。

复制代码
def sum():
  print('one')
  yield 1
  print('two')
  yield 2
  print('three')
  yield 3 
  print('end')

test = sum()
next(test)   # one
next(test)   # two
next(test)   # three
next(test)   # end  超过迭代长度,报错
复制代码

 

 斐波那契数列的各种实现方法:

def fibo(n):
  a, b, n1 = 0, 1, 0
  while n1 < n:
    a, b = b, a + b
    print(a)
    n1 = n1 + 1

fibo(5)   # 1 1 2 3 5
复制代码
import sys

class Fibo:
  def __init__(self, max):
    self.max = max
    self.a, self.b, self.n = 0,1,0
  def __iter__(self):        #迭代器,返回自己
    return self
  def __next__(self):
    if self.n < self.max:
      r = self.b
      self.a, self.b = self.b, self.a + self.b
      self.n = self.n + 1
      return r
    else:
      sys.exit(0)


for t in Fibo(5):
  print(t)      #  1 1 2 3 5
复制代码

 

复制代码
def fibo(n):
  a, b, n1 = 0, 1, 0
  while n1 < n:
    a, b = b, a + b
    yield a
    n1 = n1 + 1

for t in fibo(5):
  print(t)   # 1 1 2 3 5
复制代码

   

如何判断一个函数是否一个generator函数,使用 isgeneratorfunction 模块:

复制代码
from inspect import isgeneratorfunction

def fibo(n):
  a, b, n1 = 0, 1, 0
  while n1 < n:
    a, b = b, a + b
    yield a
    n1 = n1 + 1

print(isgeneratorfunction(fibo))    # True
复制代码

 

生成器的应用场景:

1、遍历输出多维列表(扁平化列表)

复制代码
list1 = [1,[2,3,4],5,6,[7,[8,9]]]

def flatten(list2):
  if type(list2) == list:
    for t in range(len(list2)):
      for e in flatten(list2[t]):
        yield e
  else:
    yield list2



for t in flatten(list1):
  print(t)
复制代码

 

2、切割读取文件。直接对文件对象调用read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容:

复制代码
def readFile(paths):
  block_size = 4096 * 2
  with open(paths, mode='r+') as f:
    while True:
      block = f.read(block_size)
      if block:
        yield block
      else:
        return

for t in readFile('test.txt'):
  with open('new.txt', mode='a+') as f2:
    f2.write(t)
复制代码

 

生成器支持的方法:

close() 方法: 手动关闭生成器,关闭后再次调用迭代会报异常

复制代码
def test():
  yield 1
  yield 2
  yield 3
  yield 4

t = test()

print(next(t))  # 1
print(next(t))  # 2
t.close()
print(next(t))  # 报停止迭代异常
复制代码

 

send() 方法: 用于向生成器传入某个参数,yield会根据参数返回特定的值,使用该方法第一次调用的时候必须传 None

复制代码
def test():
  value = 0
  while True:
    r = yield value
    if r == 'stop':
      break
    value = '得到某个值进行操作并返回:%s'%r

t = test()
print(t.send(None))   # 0   第一次调用必须传 None
print(t.send('a'))    #  得到某个值进行操作并返回:a
print(t.send('b'))    #  得到某个值进行操作并返回:b
print(t.send('stop')) #  停止迭代报异常
复制代码

 

throw()  方法: 用于使生成器抛出指定的异常类型,或自定义异常类型。调用该方法的时候,会消耗下一次迭代。

复制代码
def test():
  while True:
    try:
      yield '正常输出1'
      yield '正常输出2'
      print('迭代结束')
    except ValueError:
      print('值异常')
    except TypeError:
      break

t = test()
print(next(t))  # 正常输出1
print(next(t))  #正常输出2
t.throw(ValueError)   # 值异常
print(next(t))  #正常输出2
复制代码
posted @   莫离m  阅读(101)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示