python面向对象编程

2023-05-16

符合python风格的对象

先来看一个向量类的例子

class Vector2d:
    typecode = 'd'
    
    """使用两个或者一个前导下划线,把属性标记为私有的"""
    def __init__(self, x, y):
        self.__x = float(x)
        self.__y = float(y)
        
    """@property 装饰器把读值方法标记为特性"""
    @property
    def x(self):
        return self.__x
        
    @property
    def y(self):
        return self.__y
        
    """定义__iter__ 方法,把Vector2d实例变成可迭代的对象,这样才能拆包(例如,x, y = my_vector)"""
    def __iter__(self):
        return (i for i in (self.x, self.y))
        
    """repr 函数调用Vector2d 实例,得到的结果类似于构建实例的源码"""
    def __repr__(self):
        class_name = type(self).__name__
        return '{}({!r}, {!r})'.format(class_name, *self)
        
    """print 函数会调用str 函数,对Vector2d 来说,输出的是一个有序对"""
    def __str__(self):
        return str(tuple(self))
        
    """bytes 函数会调用__bytes__ 方法,生成实例的二进制表示形式"""
    def __bytes__(self):
        return (bytes([ord(self.typecode)]) + bytes(array(self.typecode, self)))
        
    """Vector2d 实例支持使用== 比较;这样便于测试"""
    def __eq__(self, other):
        return tuple(self) == tuple(other)
        
    """定义hash, 让实例成为可散列对象"""
    def __hash__(self):
        return hash(self.x) ^ hash(self.y)
    
    """abs 函数会调用__abs__ 方法,返回Vector2d 实例的模"""
    def __abs__(self):
        return math.hypot(self.x, self.y)
        
    """bool 函数会调用__bool__ 方法,如果Vector2d 实例的模为零,返回False,否则返回True"""
    def __bool__(self):
        return bool(abs(self))
        
    def angle(self):
        return math.atan2(self.y, self.x)
        
    """内置的format() 函数和str.format() 方法把各个类型的格式化方式委托给相应的
.__format__(format_spec) 方法"""
    def __format__(self, fmt_spec=''):
        if fmt_spec.endswith('p'):
            fmt_spec = fmt_spec[:-1]
            coords = (abs(self), self.angle())
            outer_fmt = '<{}, {}>'
        else:
            coords = self
            outer_fmt = '({}, {})'
        components = (format(c, fmt_spec) for c in coords)
        return outer_fmt.format(*components)
        
        """类方法使用classmethod 装饰器修饰,不用传入self 参数;相反,要通过cls 传入类本身 """
        @classmethod
        def frombytes(cls, octets):
            typecode = chr(octets[0])
    memv = memoryview(octets[1:]).cast(typecode)
    return cls(*memv)

classmethod与staticmethod

  • classmethod: 类方法定义操作类,而不是操作实例的方法,第一个参数是类本身
  • staticmethod: staticmethod装饰器也会改变方法的调用方式,但是第一个参数不是特殊的值。其实,静态方法就是普通的函数,只是碰巧在类的定义体中,而不是在模块层定义

覆盖类属性

  • python有个很独特的特性:类属性可用于为实例属性提供默认值
  • 假如我们为类实例属性复制,那么同名类属性不受影响

序列的修改、散列和切片

协议和鸭子类型

在面向对象编程中,协议是非正式的接口,只在文档中定义,在代码中不定义。例如,Python 的序列协议只需要__len__ 和__getitem__ 两个方法。任何类,只要使用标准的签名和语义实现了这两个方法,就能用在任何期待序列的地方。

import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()
    
    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                       for rank in self.ranks]
                       
    def __len__(self):
        return len(self._cards)
        
    def __getitem__(self, position):
        return self._cards[position]

切片原理

用一个例子来看看切片的原理

>>> class MySeq:
... def __getitem__(self, index):
...     return index 
...
>>> s = MySeq()
>>> s[1] 
1
>>> s[1:4] 
slice(1, 4, None)
>>> s[1:4:2] # 从1开始,到4结束,步幅为2
slice(1, 4, 2)
>>> s[1:4:2, 9] # 如果[]中有逗号,__getitem__收到的是元组
(slice(1, 4, 2), 9)
>>> s[1:4:2, 7:9] # 返回多个切片对象
(slice(1, 4, 2), slice(7, 9, None))
>>> dir(slice) # 查看slice类的属性
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__',
'__hash__', '__init__', '__le__', '__lt__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'indices', 'start', 'step', 'stop']
>>> slice(None, 10, 2).indices(5) 
(0, 5, 2)
>>> slice(-3, None, None).indices(5) 
(2, 5, 1)
>>> help(slice.indices) # 查看slice.indices方法的信息

可切片的序列

来看看,可切片的Vector类如何实现, Vector实例的切片仍然是Vector实例

from array import array
import math
import operator

class Vector:
    typecode = 'd'
    def __init__(self, components):
        # self._components 是“受保护的”实例属性,把Vector 的分量保存在一个数组中
        self._components = array(self.typecode, components) 
    
    # 支持序列属性
    def __len__(self):
        return len(self._compoents)
        
    def __getitem__(self, index):
        # 获取实例所属的类
        cls = type(self) 
        # 如果参数使slice对象
        if isinstance(index, slice): 
            return cls(self._components[index]) 
        # 如果index是整数类型
        elif isinstance(index, numbers.Integral): 
            return self._components[index] 
        # 否则抛出异常
        else:
            msg = '{cls.__name__} indices must be integers'
            raise TypeError(msg.format(cls=cls))
        
    def __iter__(self):
        return iter(self._components) 
    
    # 将Vector实例变成可散列的对象
    def __hash__(self):
        hashs = map(hash, self._compoents)
        return functools.reduce(operator.xor, hashes)

    def __eq__(self, other):
        # 对于有几千个分量来说,效率十分低下
        # return tuple(self) == tuple(other)
        if len(self) != len(other): 
            return False
            for a, b in zip(self, other): 
            if a != b: 
            return False
        return True 
        
    def __repr__(self):
        components = reprlib.repr(self._components) 
        components = components[components.find('['):-1] 
        return 'Vector({})'.format(components)
        
    def __str__(self):
        return str(tuple(self))
        
    def __bytes__(self):
        return (bytes([ord(self.typecode)]) + bytes(self._components)) 
        
    def __abs__(self):
        return math.sqrt(sum(x * x for x in self)) 
        
    def __bool__(self):
        return bool(abs(self))
        
    @classmethod
    def frombytes(cls, octets):
        typecode = chr(octets[0])
        memv = memoryview(octets[1:]).cast(typecode)
        return cls(memv) 
        
# 单个整数索引只获取分量,值为浮点数
>>> v7 = Vector(range(7))
>>> v7[-1] 
6.0
# 切片索引返回一个新Vector实例
>>> v7[1:4] 
Vector([1.0, 2.0, 3.0])
>>> v7[-1:] 
Vector([6.0])
>>> v7[1,2] 
Traceback (most recent call last):
...
TypeError: Vector indices must be integers

动态属性存取*

使用@property 装饰器可以把x 和y 标记为只读特性)。我们可以在Vector 中编写这样的特性,但这样太麻烦。特殊方法__getattr__ 提供了更好的方式。属性查找失败后,解释器会调用__getattr__方法。
简单来说,对my_obj.x 表达式,Python会检查my_obj实例有没有名为x的属性;如果没有,到类(my_obj.__class__)中查找;如果还没有,顺着继承树继续查找。如果依旧找不到,调用my_obj所属类中定义的__getattr__方法,传入self 和属性名称的字符串形式

shortcut_names = 'xyzt'
def __getattr__(self, name):
    # 获取Vector类
    cls = type(self) 
        # 如果只有一个字母,找到所在的位置
        if len(name) == 1: 
            pos = cls.shortcut_names.find(name) 
            if 0 <= pos < len(self._components):
                # 返回对应位置的元素
                return self._components[pos]
    msg = '{.__name__!r} object has no attribute
    raise AttributeError(msg.format(cls, name))
    
>>> v = Vector(range(5))
>>> v
Vector([0.0, 1.0, 2.0, 3.0, 4.0])
# 获取第一个元素
>>> v.x 
0.0
# 为v.x赋值,这个操作应该抛出异常
>>> v.x = 10 
>>> v.x #
10
# 向量的分量没变
>>> v
Vector([0.0, 1.0, 2.0, 3.0, 4.0])

示例之所以前后矛盾,是__getattr__的运作方式导致的:仅当对象没有指定名称的属性时,Python 才会调用那个方法,这是一种后备机制
可是,像v.x = 10 这样赋值之后,v 对象有x 属性了,因此使用v.x 获取x 属性的值时不会调用_getattr_ 方法了,解释器直接返回绑定到v.x 上的值,即10。另一方面,_getattr_ 方法的实现没有考虑到self._components 之外的实例属性,而是从这个属性中获取shortcut_names 中所列的“虚拟属性”。
我们改写Vector类中的属性设置逻辑,如果为单个小写字母属性赋值,我们想抛出异常,实现__setattr__方法

def __setattr__(self, name, value):
    cls = type(self)
    if len(name) == 1: 
        # 如果name是xyzt中的一个,设置错误消息
        if name in cls.shortcut_names: 
            error = 'readonly attribute {attr_name!r}'
        elif name.islower(): 
            error = "can't set attributes 'a' to 'z' in {cls_name!r}"
        else:
            error = '' 
        if error: 
            msg = error.format(cls_name=cls.__name__, attr_name=name)
            raise AttributeError(msg)
    # 默认情况下,在超类上调用__setattr__方法,提供标准行为
    super().__setattr__(name, value)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python面向对象编程 的相关文章

  • docker-compose:容器之间的 Redis 连接被拒绝

    我正在尝试设置一个 docker compose 文件 该文件旨在替换运行多个进程 RQ 工作线程 RQ 仪表板和 Flask 应用程序 的单个 Docker 容器解决方案导师 http supervisord org 主机系统是 Debi
  • goJS 下拉菜单删除项目

    我有简单的 python Flask goJS 图形应用程序 如下所示 节点和链接文本的源是从应用程序的后端加载的 我将它们设置为model modelData像这样的部分 var graphDataString JSON parse di
  • 如何从数据库模式自动生成示例 Django 应用程序?

    我正在评估概念验证应用程序的框架 该应用程序的生命周期约为 30 天 之后它将被遗忘或完全重写 我已确定要从现有数据库模式自动生成示例应用程序 然后调整视觉设计的某些方面 我看过一个演示红宝石 on Rails 它会为数据库中的每个表自动生
  • Pyjnius导入jar文件

    Pyjnius 允许您为 java 类创建 python 包装器 例如 Hardware autoclass org myapp Hardware 有没有办法像这样导入现有的 jar 文件 语法是什么样的 您可以将 jar 添加到 CLAS
  • 如何逐行替换(更新)文件中的文本

    我试图通过读取每一行 测试它 然后写入是否需要更新来替换文本文件中的文本 我不想保存为新文件 因为我的脚本已经先备份文件并对备份进行操作 这是我到目前为止所拥有的 我从 os walk 获取路径 并且保证 pathmatch var 正确返
  • Python中非常大的整数的math.pow是错误的[重复]

    这个问题在这里已经有答案了 我试图通过计算一个整数的非常大的幂来打印一个非常大的数字 尽管我的代码是正确的 但我没有观察到所需的输出 一般来说 Python解释器可以打印系统内存支持的非常大的整数 考虑到这个假设 下面是我正在运行的代码 a
  • 桨在移动时留下痕迹(Pygame Pong 游戏)[重复]

    这个问题在这里已经有答案了 我的游戏中的球拍有问题 每次我试图移动它时 桨都会留下一条 痕迹 我想这是因为我的代码没有删除旧位置的前一个桨 如果是的话怎么删除之前的呢 我应该使用blit Code import pygame sys ran
  • Python 的二进制字符串列表

    我有一个像这样的二进制字符串 1100011101 我想将其解析为一个列表 其中每个 1 或 0 块都是列表中的单独值 例如 1100011101 变成 11 000 111 0 1 您可以通过使用正则表达式而不是从中获得一点 次要 性能g
  • Web 应用程序框架:C++ 与 Python

    作为一名程序员 我熟悉 Python 和 C 我正在考虑编写自己的简单 Web 应用程序 并且想知道哪种语言更适合服务器端 Web 开发 我正在寻找一些东西 它必须是直观的 我认识到 Wt 存在并且它遵循 Qt 的模型 我讨厌 Qt 的一件
  • 为什么Python 3中实例方法可以作为类方法调用?

    考虑下面的类 class Foo object def bar self print self 在Python 2中 2 7 13 调用bar 作为类方法引发异常 gt gt gt Foo bar hello Traceback most
  • 提取二值图像中的最中心区域

    我正在处理二进制图像 之前使用此代码来查找二进制图像中的最大区域 Use the hue value to convert to binary thresh 20 thresh thresh img cv2 threshold h thre
  • Python 宏:用例?

    如果 Python 有一个类似于 Lisp Scheme 的宏工具 比如元Python https code google com p metapython 你会如何使用它 如果您是一名 Lisp Scheme 程序员 您会使用宏来做什么
  • Python 多处理:全局对象未正确复制到子级

    前几天我回答了一个关于SO的问题 https stackoverflow com q 67047533 1925388关于并行读取 tar 文件 这是问题的要点 import bz2 import tarfile from multipro
  • 在 CSV 文件的最上面一行写入

    我有这个sample csv 文件 a 1 apple b 2 banana c 3 cranberry d 4 durian e 5 eggplant 并有以下代码 samplefile open sample csv rb rows s
  • 如何使 cx-oracle 将查询结果绑定到字典而不是元组?

    这是我的代码 我想找到一种方法将查询结果作为字典列表而不是元组列表返回 看起来 cx oracle 通过部分文档讨论 绑定 来支持这一点 虽然我不知道它是如何工作的 def connect dsn cx Oracle makedsn hos
  • 无法从源 pylance 解析导入烧瓶

    我正在学习 Python 课程的一部分是使用 Flask 设置网络服务器 我按照 Flask 安装文档执行了步骤 由于某种原因 flask 模块带有下划线 如下所示 当我将鼠标悬停时 我会得到如下附加信息 无法从源 pylance 解析导入
  • 类型错误:不可散列的类型:pandas 的“切片”

    我有一个 pandas 数据结构 我这样创建 test inputs pd read csv input test csv delimiter 它的形状 print test inputs shape is this 28000 784 我
  • Python,质数检查器[重复]

    这个问题在这里已经有答案了 你好 我正在创建一个函数来检查一个数字是否是素数 但它告诉我 9 是一个素数 def eprimo num if num lt 2 return False if num 2 return True else f
  • 从 Python 访问 802.11 无线管理帧

    我想从 Linux 上的 Python 嗅探 802 11 管理 探测请求 帧 这可以从 Scapy 中实现 如下所示 coding utf 8 from scapy all import def proc p if p haslayer
  • 根据键的重复值从列表中删除字典

    我是Python新手 假设我有以下字典列表 mydictList a 1 b 2 c 3 a 2 b 2 c 4 a 2 b 3 c 4 从上面的列表中 我想删除具有相同键 b 值的字典 所以结果列表应该是 mydictList a 1 b

随机推荐

  • 结构体继承自结构体

    C 43 43 Code 功能 xff1a 结构体继承自结构体 结论 xff1a 1 结构体可以继承自结构体 2 结构体同样有构造函数和析构函数 include lt iostream gt using namespace std stru
  • 【STM32L4】FreeRTOS消息队列三串口接收发送

    环境 STM32L476G DISCO 开发板STM32CubeIDE 1 1 0STM32CubeMX 5 4 0 STM32CubeIDE配置 移植FreeRTOS xff0c 选择CMSIS V2 FreeRTOS的所有配置的选择默认
  • OpenCV如何获取视频当前的一帧图像

    xff08 OpenCV读取视频 OpenCV提取视频每一帧 每一帧图片合成新的AVI视频 xff09 CvCapture 是视频获取结构 被用来作为视频获取函数的一个参数 比如 CvCapture cap IplImage cvQuery
  • 无人机、无人车仿真器AirSim中激光雷达Lidar设置

    1 在车辆或无人机中激活激光雷达 默认情况下 xff0c 激光雷达是不开启的 xff0c 如果需要开启激光雷达 xff0c 则需要在settings jason文件中进行激活 xff0c settings jason 文件位于 Docume
  • 软件项目产品化之路

    软件项目产品化之路 2 产品化之路 2 1 困惑 软件项目产品化是大量软件企业 xff0c 特别是应用型软件研发企业所必须面临的问题 不论是小型的软件公司和中大型的软件企业 xff0c 在面对软件项目和软件产品 xff0c 都有诸多困惑 到
  • 软件产品化的一些见解

    软件产品化的定义 软件产品化 即客户无需为软件添加或调整代码和语句即能完成软件的安装配置 应用初始化 系统管理 用户使用的全过程 并且软件至少能满足80 以上的用户某一组应用需求 软件产品化只是完成了产品的生产环节 后面的产品销售 市场推广
  • 程序员与工匠精神

    前几天和一个朋友聊天时说 xff1a 我有强迫症 xff0c 每次看到不符合自己审美的代码时 xff0c 总想重构一下 朋友的观点与大多数人相仿 xff0c 程序只要满足要求 xff0c 运行正确就可以 在现实的工作中一样 xff0c 每当
  • linux 进程线程思维导图

  • 修改结构体中成员的值

    include lt iostream gt using namespace std struct student char name 10 float grade 更改student数据的grade成员 xff0c 参数形式为引用 voi
  • C++中冒号的用法

    1 冒号 xff08 xff09 用法 xff08 1 xff09 表示机构内位域的定义 xff08 即该变量占几个bit空间 xff09 typedef struct XXX unsigned char a 4 unsigned char
  • C/C++预处理指令

    本文主要记录了C C 43 43 预处理指令 xff0c 常见的预处理指令如下 xff1a 空指令 xff0c 无任何效果 include包含一个源代码文件 define定义宏 undef取消已定义的宏 if如果给定条件为真 xff0c 则
  • STM32F1常用外设介绍(超详细35000字介绍)

    STM32学习笔记 GPIO配置步骤 步骤 xff1a 第一步 xff0c 使用RCC开启GPIO的时钟 第二步 xff0c 使用GPIO Init 函数初始化GPIO 第三步 xff0c 使用输出或者输入的函数控制GPIO口 常用的RCC
  • MFC拷贝文件及进度条显示

    参考 xff1a 封装CopyFileEx函数 xff0c 实现文件复制中的暂停 xff0c 控速 xff0c 获取进度 http blog csdn net career2011 article details 6844513 实例讲解C
  • 什么是寄存器?(STM32)

    什么是寄存器 xff1f 我们现在在开发STM32时 xff0c 已经很少用到寄存器编程 xff0c 更多的使用ST公司所提供的标准库和最新的HAL库进行编程实现 xff0c 但是不管是标准库还是HAL库都是在原来的寄存器层面上进行了封装
  • 计算机网络——物理层(一)

    物理层 xff08 部分 xff09 机械特性 xff1a 指明接口所用接线器的形状和尺寸 引脚数目和排列 固定和锁定装置等 电气特性 xff1a 指明在接口电缆的各条线上出现的电压的范围 功能特性 xff1a 指明某条线上出现的某一电平的
  • 洛谷P5717-三角形分类

    洛谷P5717 三角形分类 题目 这道题更像是初中题 xff0c 但是怎么能完整的按照题目的意思来解决呢 xff0c 说实话这个题卡了我有一会儿 xff0c 要做一次性做出这个题 xff0c 我觉得需要搞清楚if if 和if else i
  • 洛谷P1424-小鱼的航程(改进版)

    洛谷P1424 小鱼的航程 xff08 改进版 xff09 这个题我第一次做的时候 xff0c 有两个没过 xff0c 后来检查的时候发现原来是没有考虑开始的时间是不是周六周日 xff0c 如果是周六要在原来的天数上 2 xff0c 如果是
  • freertos-简介(一)

    FreeRTOS 裸机 不带任何操作系统 只能先打完游戏回复信息 实时性差 xff0c 程序轮流执行delay空等待 xff0c CPU不执行其他代码结构臃肿 xff0c 实现功能都在while循环 RTOS 实时操作系统 会执行打游戏一个
  • PCB设计过程中AD使用流程详解(超详细)

    PCB设计过程中AD使用流程详解 xff08 超详细 xff09 1 设计前期部分 规则设定 xff1a Preference system file type关联文件 xff08 所有关联 xff09 PCB editor General
  • python面向对象编程

    符合python风格的对象 先来看一个向量类的例子 span class token keyword class span span class token class name Vector2d span span class token