全面深入了解python(一)

2023-10-30

全面深入了解python(一)

写在开始前,此教程不是基础教程,在看之前你需要有一定的python基础,不然你可能无法理解教程到底教了哪些东西。

环境:python版本是3.6.5(>=3.4即可)

1. Python数据模型

数据模型其实是对Python框架的描述,它规范了这门语言自身构建模块的接口,这些模块包括但不限于序列、迭代器、函数、类和上下文管理器。

Python解释器碰到特殊的句法时,会使用特殊方法去激活一些基本的对象操作,这些特殊方法的名字以两个下划线开头,以两个下划线结尾(例如__getitem__)。比如obj[key]的背后就是__getitem__方法,为了能求得my_collection[key]的值,解释器实际上会调用my_collection.__getitem__(key)

上面说的特殊方法其实有个昵称,可能你以前就听过,叫做魔术方法。这些魔术方法能让你自己的对象实现和支持以下的语言结构,并与之交互:

  • 迭代
  • 集合类
  • 属性访问
  • 运算符重载
  • 函数和方法的调用
  • 对象的创建和销毁
  • 字符串表示形式和格式化
  • 管理上下文(即with块)

1.1 一叠Python风格的纸牌

接下来用一个非常简单的例子来展示如何实现__getitem____len__这两个特殊方法,通过这个例子我们也能见识到特殊方法的强大。

import collections

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

class OrderCard:
    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]

首先,我们用collections.namedtuple构建了一个简单的类来表示一张纸牌。自Python 2.6开始,namedtuple就加入到Python里,用以构建只有少数属性但是没有方法的对象,比如数据库条目。使用终端进行样例的输入,利用namedtuple,我们可以很轻松地得到一个纸牌对象:

>>> beer_card = Card('7','diamonds')
>>> beer_card
Card(rank='7', suit='diamonds')

当然,我们这个例子主要还是关注OrderCard这个类,很短小精悍。首先,它跟任何标准Python集合类型一样,可以用len()函数来查看一叠牌有多少张:

>>> order_card = OrderCard()
>>> len(order_card)
52

从一叠牌中抽取特定的一张纸牌,比如说第一张或者最后一张,是很容易的:order_card[0]或order_card[-1]。这都是由__getitem__方法提供的:

>>> order_card[0]
Card(rank='2', suit='spades')
>>> order_card[-1]
Card(rank='A', suit='hearts️')

我们需要单独写一个方法用来随机抽取一张纸牌吗?没必要,Python已经内置了从一个序列中随机选出一个元素的函数random.choice,我们直接把它用在这一叠纸牌实例上就好:

>>> from random import choice
>>> choice(order_card)
Card(rank='8', suit='spades')
>>> choice(order_card)
Card(rank='3', suit='hearts️')
>>> choice(order_card)
Card(rank='Q', suit='hearts️')

现在已经可以体会到通过实现魔术方法来利用Python数据模型的两个好处。

  • 作为你的类的用户,他们不必去记住标准操作的各种名称(“怎么得到元素的总数?是.size()还是.length()还是别的什么?”)
  • 可以更加方便地利用Python的标准库,比如random.choice函数,从而不用重新发明轮子。

因为__getitem__方法把[]操作交给了self._cards列表,所以我们的deck类自动支持切片(slicing)操作。下面列出了查看一叠牌最上面3张和只看排面是K的牌的操作。其中第二种操作的具体方法是,先抽出索引是11的那张牌,然后每隔13张牌拿1张:

>>> order_card[:3]
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
>>> order_card[11::13]#前面是索引,后面是步长
[Card(rank='K', suit='spades'), Card(rank='K', suit='diamonds️'), Card(rank='K', suit='clubs️'), Card(rank='K', suit='hearts️')]

另外,仅仅实现了__getitem__方法,这一叠牌就变成可迭代的了:

>>> for card in order_card:
...     print(card)
Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
...

反向迭代也没关系:

>>> for card in reversed(order_card):
...     print(card)
Card(rank='A', suit='hearts️')
Card(rank='K', suit='hearts️')
Card(rank='Q', suit='hearts️')
...

迭代通常是隐式的,比如说一个集合类型没有实现__contains__方法,那么in运算符就会按顺序做一次迭代搜索。于是,in运算符可以用在我们的OrderCard类上,因为它是可迭代的:

>>> Card('3','spades') in order_card
True
>>> Card('K','test') in order_card
False

那么排序呢?我们用点数来判定扑克牌的大小,2最小、A最大;同时还要加上对花色的判定,黑桃最大、红桃次之、方块再次、梅花最小。下面就是按照这个规则来给扑克牌排序的函数,梅花2的大小是0,黑桃A是51:

suit_values = dict(spades=3,hearts=2,diamonds=1,clubs=0)

def spades_high(card):
    rank_value = OrderCard.ranks.index(card.rank)
    return rank_value * len(suit_values) + suit_values[card.suit]

有了spades_high函数,就能对这叠牌进行升序排序了:

>>> for card in sorted(order_card,key=spades_high):
...     print(card)
Card(rank='2', suit='clubs')
Card(rank='2', suit='diamonds')
Card(rank='2', suit='hearts')
... 
Card(rank='A', suit='diamonds')
Card(rank='A', suit='hearts')
Card(rank='A', suit='spades')

虽然OrderCard隐式地继承了object类,但功能却不是继承而来的。我们通过数据模型和一些合成来实现这些功能。通过实现__len____getitem__这两个特殊方法,OrderCard就跟一个Python自由的序列数据类型一样,可以体现出Python的核心语言特性(例如迭代和切片)。同时这个类还可以用于标准库中如:random.choice、reversed和sorded这些函数。另外,对合成的运用使__len____getitem__的具体实现可以代理给self._cards

这个python列表(即list对象)。

1.2 如何使用特殊方法(魔术方法)

首先明确一点,魔术方法的存在是为了被python解释器调用的,你自己并不需要调用它们。也就是说没有my_object.__len__()这种写法,而应该使用len(my_object)。在执行len(my_object)的时候,如果my_object是一个自定义类的对象,那么python会自己去调用其中由你实现的__len__方法。

通常代码中无需直接使用魔术方法,除非有大量的元编程存在,唯一的例外是经常使用__init__方法,目的是在代码的子类的__init__方法中调用超类的构造器。通过内置函数(len、iter、str等等)来使用魔术方法是最好的选择,这些内置函数不仅会调用魔术方法,对于内置类来说,运行它们速度更快。

还有一点,不要自己想当然的随意添加魔术方法,比如__foo__之类的,因为现在没有被Python内部使用,不代表以后不被用。

来实现一个二维向量(vector)类,这里的向量就是欧几里得几何中常用的概念,常在数学和物理中使用。
h1
python内置的complex类可以用来表示向量,但是我们自定义的类可以扩展到n维向量。

自定义Vector类:

from math import hypot

class Vector:
    def __init__(self,x=0,y=0):
        self.x = x
        self.y = y

    def __repr__(self):
        return f'Vector({self.x},{self.y})' # 3.6新功能,新的格式化方式

    def __abs__(self):
        return hypot(self.x,self.y)

    def __bool__(self):
        return bool(abs(self))

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Vector(x,y)

    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)

把上图的样例实现出来:

>>> v1 = Vector(2,4)
>>> v2 = Vector(2,1)
>>> v1 + v2
Vector(4,5)

通过+运算符所得到的结果也是一个向量,而且结果会被控制台友善的打印出来。

abs是一个内置函数,如果输入是整数或者浮点数,返回值是输入值的绝对值,输入值是复数,返回值应该是该复数的模,因此定义该函数时,也应该返回该向量的模:

>>> v = Vector(3,4)
>>> abs(v)
5.0

我们还可以利用*运算符来实现向量的标量乘法:

>>> v * 3
Vector(9,12)
>>> abs(v * 3)
15.0

实现的Vector类中是由:__repr____abs____add____mul__这些特殊方法实现。但是在上述使用中发现除了__init__会被使用之外,其他的魔术方法是被python解释器直接调用,而不是自己使用代码调用。(上文已经提过)

1.3 为什么len不是普通方法

如果x是一个内置类型的实例,那么len(x)的速度会非常快,背后的原因是CPython会直接从一个C结构体里读取对象的长度,完全不会调用任何方法。获取一个集合中元素的数量是一个很常见的操作,在str、list、memoryview等类型上,这个操作必须高效。

c语言中的结构体里可以存储这个对象的相关数据信息,是其属性,可以直接读取。例:

struct Articles
{
   char  title[50];  //文章标题
   char  author[50];  //文章作者
   char  subject[100];  //文章主题
   int   article_length;  //文章长度
   int   article_id;  //文章编号
} article;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

全面深入了解python(一) 的相关文章

随机推荐

  • python笔记4--python常见功能函数和问题

    python笔记4 python常见功能函数和问题 不知不觉用python好久了 期间也遇见了形形色色的问题 平时这里一份文档那里一份笔记 要用的时候总会发现有些文档一时半会找不到 索性把曾经遇见的和以后遇见的都陆续归个档吧 以便于有需要的
  • E - Entertainment Box

    Ada Bertrand and Charles often argue over which TV shows to watch and to avoid some of their fights they have finally de
  • C语言const限定词基本用法

    它限定一个变量不允许被改变 产生静态作用 使用const在一定程度上可以提高程序的安全性和可靠性 另外 在观看别人代码的时候 清晰理解const所起的作用 对理解对方的程序也有一定帮助 1 作用 防止误操作只读内存 像这种 Hello 字符
  • git拉取dev分支及git的基本常用命令

    新项目必做的操作 就是拉取远程仓库的代码 一般的开发是在dev分支上开发 但是默认拉下来了master分支 需要拉取dev分支怎么拉取呢 拉取dev分支代码 指定分支也可以 1 首先进入到你需要拉代码的文件里面 如果新项目第一次拉代码建议新
  • pandas的read_excel 报错:OverflowError: date value out of range‘ 的解决办法

    pandas的read excel 报错 OverflowError date value out of range 的解决办法 首先 报错了要进行原因分析 导致这个错误的原因是将文件中的数值读成了日期 解决办法 尝试1 pip insta
  • 关于Android无法用SmsManager发送短信的问题

    关于Android无法用SmsManager发送短信的问题 我使用的是如下代码 SmsManager smsManager SmsManager getDefault PendingIntent pi PendingIntent getAc
  • 解决element-ui中table表格row-style改变当前行样式失效问题

    前言 这个问题点 要注意的是element ui版本 版本不同解决方案也不同 我也收到好多私信说没解决 后来自己测试了一下 发现的确受版本影响 大家使用时仔细看element ui文档中的 是不是object格式的 最近在使用vue ele
  • 操作系统接口--OS

    操作系统接口 接口 连接两个东西 信号转换 屏蔽细节 接口需要实现什么 要知道调用它的是什么东西 背后是怎么完成这件事情的 计算机调用接口的三种方式 1 命令行 命令行 其实就是一个c函数 当我们操作系统启动后 会执行一个函数 该函数在目录
  • 【深度学习】logistic回归模型

    目录 神经网络 数据 符号准备 logistic回归 损失函数和代价函数 梯度下降法 向量化 神经网络 我们学习深度学习的目的就是用于去训练神经网络 而神经网络是什么呢 我们先来看下面一个基础的生物上的神经元的图片 通常来说 神经元就是处理
  • feign 传输文件

    1 pom文件
  • android源码分析!程序员怎样优雅度过35岁中年危机?送大厂面经一份!

    最近跟我的一些读者交流 有一位读者的经历让我记忆深刻 有一次和大学同学聚会 和几个在BAT的同学聊了聊技术 发现自己在创业公司这几年 完全是吃老本的状态 没有什么机会精进技术 同样是工作了三年 和同学的差距越来越大 我继续问他 他说真正让他
  • StringBuilder的用法

    StringBuilder简介 StringBuilder 最早出现在JDK1 5 是一个字符拼接的工具类 它和StringBuffer一样都继承自父类AbstractStringBuilder 在AbstractStringBuilder
  • “华为杯”研究生数学建模竞赛2020年-【华为杯】B题:降低汽油精制过程中的辛烷值损失模型(附优秀论文及python代码)

    目录 摘 要 1 问题重述 1 1 问题背景 1 2 需要解决问题 2 问题假设 3 符号说明
  • 第二章 感知机

    感知机 perceptron 是二类分类的线性分类模型 它包括输入空间 输出空间 模型结构 参数空间和假设空间 感知机学习旨在求出将训练数据进行线性划分的分离超平面 为此导入基于误分类的损失函数 利用梯度下降法对损失函数进行极小化 求得感知
  • STL的一些基本背景了解。

    STL源代码的头文件一般都是内联模式的 现在简单的把stl的集中类型进行分类说明一下 1 容器类 一般分为关联式容器和顺序式容器 典型的例子的就是vector为典型的顺序式容器 对于stl来说主要采用向量 链表 二叉树以及他们的组合为底层存
  • pandas中iloc()函数

    pandas中iloc 函数 DataFrame iloc 纯基于整数位置的索引 import pandas as pd mydict a 1 b 2 c 3 d 4 a 100 b 200 c 300 d 400 a 1000 b 200
  • TensorFlow数据归一化

    TensorFlow数据归一化 1 tf nn l2 normalize l2 normalize x dim epsilon 1e 12 name None output x sqrt max sum x 2 epsilon 2 使用sc
  • 【送书活动】深入浅出SSD:固态存储核心技术、原理与实战

    前言 作者主页 雪碧有白泡泡 个人网站 雪碧的个人网站 推荐专栏 java一站式服务 React从入门到精通 前端炫酷代码分享 从0到英雄 vue成神之路 uniapp 从构建到提升 从0到英雄 vue成神之路 解决算法 一个专栏就够了 架
  • 博士研究生如何做创新性研究?(蒲慕明院士)

    读文献不要只看文献描述的工作 What was done 还有四个 W 你也应知道 是谁做的 Who did it 什么时候做的 When 在哪里做的 Where 为什么会做这工作 Why 想了解重要的创新工作的来龙去脉 你就要读科学史 读
  • 全面深入了解python(一)

    全面深入了解python 一 写在开始前 此教程不是基础教程 在看之前你需要有一定的python基础 不然你可能无法理解教程到底教了哪些东西 环境 python版本是3 6 5 gt 3 4即可 1 Python数据模型 数据模型其实是对P