我的个人博客排版更舒服: https://www.luozhiyun.com/archives/264

列表和元组

  • 列表是动态的,长度大小不固定,可以随意地增加、删减或者改变元素(mutable)。
  • 而元组是静态的,长度大小固定,无法增加删减或者改(immutable)。

如果你想对已有的元组做任何"改变”,那就只能重新开辟一块内存,创建新的元组了。

如下:

tup = (1, 2, 3, 4)
new_tup = tup + (5, ) # 创建新的元组new_tup,并依次填充原元组的值 new _tup (1, 2, 3, 4, 5) l = [1, 2, 3, 4]
l.append(5) # 添加元素5到原列表的末尾 l [1, 2, 3, 4, 5]

列表和元组存储方式的差异

由于列表是动态的,所以它需要存储指针,来指向对应的元素。另外,由于列表可变,所以需要额外存储已经分配的长度大小,这样才可以即使扩容。

l = []
l.__sizeof__() // 空列表的存储空间为40字节
40
l.append(1) l.__sizeof__()
72 // 加⼊了元素1之后,列表为其分配了可以存储4个元素的空间 (72 - 40)/8 = 4
l.append(2)
l.__sizeof__()
72 // 由于之前分配了空间,所以加⼊元素2,列表空间不变
l.append(3)
l.__sizeof__()
72 // 同上
l.append(4)
l.__sizeof__()
72 // 同上
l.append(5)
l.__sizeof__()
104 // 加⼊元素5之后,列表的空间不⾜,所以⼜额外分配了可以存储4个元素的空间

但是对于元组,情况就不同了。元组长度大小固定,元素不可变,所以存储空间固定。

列表和元组的性能

元组要比列表更加轻量级一些,所以总体上来 说,元组的性能速度要略优于列表。

Python会在后台,对静态数据做一些资源缓存资源缓存(resource caching)。通常来说,因为垃圾回收机制 的存在,如果一些变量不被使用了,Python就会回收它们所占用的内存,返还给操作系统,以便其他变量 或其他应用使用。

但是对于一些静态变量,比如元组,如果它不被使用并且占用空间不大时,Python会暂时缓存这部分内存。

由下面例子元组的初始化速 度,要比列表快5倍。

python3 -m timeit 'x=(1,2,3,4,5,6)'
20000000 loops, best of 5: 9.97 nsec per loop
python3 -m timeit 'x=[1,2,3,4,5,6]'
5000000 loops, best of 5: 50.1 nsec per loop

字典和集合

集合和字典基本相同,唯一的区别,就是集合没有键和值的配对,是一系列无序的、唯一的元素组合。

如何访问、使用就不说了,说两个注意点:

  1. Python 中字典和集合,无论是键还是值,都可以是混合类型
s = {1, 'hello', 5.0}
  1. 字典访问可以直接索引键,如果不存在,就会抛出异常;也可以使用 get(key, default) 函数来进行索引。如果键不存在,调用 get() 函数可以返回一个默认值。
d = {'name': 'jason', 'age': 20}
d['name']
'jason'
d['location']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'location' d = {'name': 'jason', 'age': 20}
d.get('name')
'jason'
d.get('location', 'null')
'null

字典和集合的工作原理

字典和集合的内部结构都是一张哈希表。

  • 对于字典而言,这张表存储了哈希值(hash)、键和值这 3 个元素。
  • 而对集合来说,区别就是哈希表内没有键和值的配对,只有单一的元素了。

老版本 Python 的哈希表结构如下所示:

--+-------------------------------+
| 哈希值 (hash) 键 (key) 值 (value)
--+-------------------------------+
0 | hash0 key0 value0
--+-------------------------------+
1 | hash1 key1 value1
--+-------------------------------+
2 | hash2 key2 value2
--+-------------------------------+
. | ...
__+_______________________________+

随着哈希表的扩张,它会变得越来越稀疏。举个例子:

{'name': 'mike', 'dob': '1999-01-01', 'gender': 'male'}

那么它会存储为类似下面的形式:

entries = [
['--', '--', '--']
[-230273521, 'dob', '1999-01-01'],
['--', '--', '--'],
['--', '--', '--'],
[1231236123, 'name', 'mike'],
['--', '--', '--'],
[9371539127, 'gender', 'male']
]

这样的设计结构显然非常浪费存储空间。

为了提高存储空间的利用率,现在的哈希表除了字典本身的结构,会把索引和哈希值、键、值单独分开:

Indices
----------------------------------------------------
None | index | None | None | index | None | index ...
---------------------------------------------------- Entries
--------------------
hash0 key0 value0
---------------------
hash1 key1 value1
---------------------
hash2 key2 value2
---------------------
...
---------------------

在新的哈希表结构下的存储形式,上面的例子就是这样:

indices = [None, 1, None, None, 0, None, 2]
entries = [
[1231236123, 'name', 'mike'],
[-230273521, 'dob', '1999-01-01'],
[9371539127, 'gender', 'male']
]

插入操作

每次向字典或集合插入一个元素时,Python 会首先计算键的哈希值(hash(key)),再和 mask = PyDicMinSize - 1 做与操作,计算这个元素应该插入哈希表的位置 index = hash(key) & mask。

如果哈希表中此位置是空的,那么这个元素就会被插入其中。而如果此位置已被占用,Python 便会比较两个元素的哈希值和键是否相等。

若两者中有一个不相等,这种情况我们通常称为哈希冲突(hash collision),意思是两个元素的键不相等,但是哈希值相等。这种情况下,Python 便会继续寻找表中空余的位置,直到找到位置为止。

删除操作

对于删除操作,Python 会暂时对这个位置的元素,赋于一个特殊的值,等到重新调整哈希表的大小时,再将其删除。

为了保证其高效性,字典和集合内的哈希表,通常会保证其至少留有 1/3 的剩余空间。随着元素的不停插入,当剩余空间小于 1/3 时,Python 会重新获取更大的内存空间,扩充哈希表。

函数变量作用域

局部变量

如果变量是在函数内部定义的,就称为局部变量,只在函数内部有效。一旦函数执行完毕,局部变量就会被回收,无法访问。

对于嵌套函数来说,内部函数可以访问外部函数定义的变量,但是无法修改,若要修改,必须加上 nonlocal 这个关键字:

def outer():
x = "local"
def inner():
nonlocal x # nonlocal 关键字表示这里的 x 就是外部函数 outer 定义的变量 x
x = 'nonlocal'
print("inner:", x)
inner()
print("outer:", x)
outer()
# 输出
inner: nonlocal
outer: nonlocal

如果不加上 nonlocal 这个关键字,而内部函数的变量又和外部函数变量同名,那么同样的,内部函数变量会覆盖外部函数的变量。

def outer():
x = "local"
def inner():
x = 'nonlocal' # 这里的 x 是 inner 这个函数的局部变量
print("inner:", x)
inner()
print("outer:", x)
outer()
# 输出
inner: nonlocal
outer: local

全局变量

全局变量则是定义在整个文件层次上的,可以在文件内的任何地方被访问,但是不能在函数内部随意改变全局变量的值。

例如:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
...
MIN_VALUE += 1
...
validation_check(5) #输出
UnboundLocalError: local variable 'MIN_VALUE' referenced before assignment

因为,Python 的解释器会默认函数内部的变量为局部变量,但是又发现局部变量 MIN_VALUE 并没有声明,因此就无法执行相关操作。

如果我们一定要在函数内部改变全局变量的值,就必须加上 global 这个声明:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
global MIN_VALUE
...
MIN_VALUE += 1
...
validation_check(5)

如果遇到函数内部局部变量和全局变量同名的情况,那么在函数内部,局部变量会覆盖全局变量,比如下面这种:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
MIN_VALUE = 3
...

闭包

闭包中外部函数返回的是一个函数,返回的函数通常赋于一个变量,这个变量可以在后面被继续执行调用。

比如,我们想计算一个数的 n 次幂,用闭包可以写成下面的代码:

def nth_power(exponent):
def exponent_of(base):
return base ** exponent
return exponent_of # 返回值是 exponent_of 函数 square = nth_power(2) # 计算一个数的平方
cube = nth_power(3) # 计算一个数的立方
square
# 输出
<function __main__.nth_power.<locals>.exponent(base)> cube
# 输出
<function __main__.nth_power.<locals>.exponent(base)> print(square(2)) # 计算 2 的平方
print(cube(2)) # 计算 2 的立方
# 输出
4 # 2^2
8 # 2^3

这里外部函数 nth_power() 返回值,是函数 exponent_of(),而不是一个具体的数值。

面对对象

函数

静态函数:与类没有什么关联可以用来做一些简单独立的任务,既方便测试,也能优化代码结构。静态函数可以通过在函数前一行加上 @staticmethod 来表示。

类函数:第一个参数一般为 cls,表示必须传一个类进来。类函数最常用的功能是实现不同的 init 构造函数,类似java中的构造器。类函数需要装饰器 @classmethod 来声明。

成员函数:是我们最正常的类的函数,它不需要任何装饰器声明,第一个参数 self 代表当前对象的引用,可以通过此函数,来实现想要的查询 / 修改类的属性等功能。

例子如下:

class Document():

    WELCOME_STR = 'Welcome! The context for this book is {}.'

    def __init__(self, title, author, context):
print('init function called')
self.title = title
self.author = author
self.__context = context # 类函数
@classmethod
def create_empty_book(cls, title, author):
return cls(title=title, author=author, context='nothing') # 成员函数
def get_context_length(self):
return len(self.__context) # 静态函数
@staticmethod
def get_welcome(context):
return Document.WELCOME_STR.format(context) empty_book = Document.create_empty_book('What Every Man Thinks About Apart from Sex', 'Professor Sheridan Simove') print(empty_book.get_context_length())
print(empty_book.get_welcome('indeed nothing')) ########## 输出 ########## init function called
7
Welcome! The context for this book is indeed nothing.

继承

class Entity():
def __init__(self, object_type):
print('parent class init called')
self.object_type = object_type def get_context_length(self):
raise Exception('get_context_length not implemented') def print_title(self):
print(self.title) class Document(Entity):
def __init__(self, title, author, context):
print('Document class init called')
Entity.__init__(self, 'document')
self.title = title
self.author = author
self.__context = context def get_context_length(self):
return len(self.__context) class Video(Entity):
def __init__(self, title, author, video_length):
print('Video class init called')
Entity.__init__(self, 'video')
self.title = title
self.author = author
self.__video_length = video_length def get_context_length(self):
return self.__video_length harry_potter_book = Document('Harry Potter(Book)', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')
harry_potter_movie = Video('Harry Potter(Movie)', 'J. K. Rowling', 120) print(harry_potter_book.object_type)
print(harry_potter_movie.object_type) harry_potter_book.print_title()
harry_potter_movie.print_title() print(harry_potter_book.get_context_length())
print(harry_potter_movie.get_context_length()) ########## 输出 ########## Document class init called
parent class init called
Video class init called
parent class init called
document
video
Harry Potter(Book)
Harry Potter(Movie)
77
120

我们可以从中抽象出一个叫做 Entity 的类,来作为Document 和 Video的父类。

每个类都有构造函数,继承类在生成对象的时候,是不会自动调用父类的构造函数的,因此你必须在 init() 函数中显式调用父类的构造函数。它们的执行顺序是 子类的构造函数 -> 父类的构造函数。

由于父类的get_context_length方法是用来被重写的,所以使用 Entity 直接生成对象,调用 get_context_length() 函数,就会 raise error 中断程序的执行。

继承的优势:减少重复的代码,降低系统的熵值(即复杂度)。

抽象类

抽象类是一种特殊的类,它生下来就是作为父类存在的,一旦对象化就会报错。同样,抽象函数定义在抽象类之中,子类必须重写该函数才能使用。相应的抽象函数,则是使用装饰器 @abstractmethod 来表示。

抽象类就是这么一种存在,它是一种自上而下的设计风范,你只需要用少量的代码描述清楚要做的事情,定义好接口,然后就可以交给不同开发人员去开发和对接。

from abc import ABCMeta, abstractmethod

class Entity(metaclass=ABCMeta):
@abstractmethod
def get_title(self):
pass @abstractmethod
def set_title(self, title):
pass class Document(Entity):
def get_title(self):
return self.title def set_title(self, title):
self.title = title document = Document()
document.set_title('Harry Potter')
print(document.get_title()) entity = Entity() ########## 输出 ########## Harry Potter ---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-266b2aa47bad> in <module>()
21 print(document.get_title())
22
---> 23 entity = Entity()
24 entity.set_title('Test') TypeError: Can't instantiate abstract class Entity with abstract methods get_title, set_title

代码中entity = Entity()直接报错,只有通过 Document 继承 Entity 才能正常使用。

python学习要点(一)的更多相关文章

  1. python学习要点(二)

    我的博客:https://www.luozhiyun.com/archives/269 '==' VS 'is' '=='操作符比较对象之间的值是否相等. 'is'操作符比较的是对象的身份标识是否相等 ...

  2. coursera python 学习总结

    为啥要写这篇总结?早上突然想到了四个字:知行合一.实践,总结,再实践,再总结经验,积累经验,为己所用.闲话少叙,来干货: 1.目标要单一,如果想要完成课程,还要健身,还要玩玩游戏.看看电影,还学别的课 ...

  3. Python学习之路【第一篇】-Python简介和基础入门

    1.Python简介 1.1 Python是什么 相信混迹IT界的很多朋友都知道,Python是近年来最火的一个热点,没有之一.从性质上来讲它和我们熟知的C.java.php等没有什么本质的区别,也是 ...

  4. Deep learning with Python 学习笔记(1)

    深度学习基础 Python 的 Keras 库来学习手写数字分类,将手写数字的灰度图像(28 像素 ×28 像素)划分到 10 个类别 中(0~9) 神经网络的核心组件是层(layer),它是一种数据 ...

  5. Python 学习参考书目推荐

    Python 学习,参考书目推荐 前言 好的技术书籍可以帮助我们快速地成长,大部分人或多或少地受益于经典的技术书籍.在「Python开发者」微信公号后台,我们经常能收到让帮忙推荐书籍的消息.这类的问题 ...

  6. Python学习--04条件控制与循环结构

    Python学习--04条件控制与循环结构 条件控制 在Python程序中,用if语句实现条件控制. 语法格式: if <条件判断1>: <执行1> elif <条件判断 ...

  7. Python学习--01入门

    Python学习--01入门 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.和PHP一样,它是后端开发语言. 如果有C语言.PHP语言.JAVA语言等其中一种语言的基础,学习Py ...

  8. Python 学习小结

    python 学习小结 python 简明教程 1.python 文件 #!/etc/bin/python #coding=utf-8 2.main()函数 if __name__ == '__mai ...

  9. Python学习路径及练手项目合集

    Python学习路径及练手项目合集 https://zhuanlan.zhihu.com/p/23561159

随机推荐

  1. Hexo站点Next主题添加google adsense广告

    本文转载自: https://www.93bok.com 前言 无意之间看到了google adsense的广告,于是就想到给我的站点也弄一个,本来以为是很简单的事,参考了很多资料,终于是部署成功了, ...

  2. Python爬虫抓取微博评论

    第一步:引入库 import time import base64 import rsa import binascii import requests import re from PIL impo ...

  3. PySide2的This application failed to start because no Qt platform plugin could be initialized解决方式

    解决PySide2的This application failed to start because no Qt platform plugin could be initialized问题 今天在装 ...

  4. 2019年最新老男孩高性能Web架构与自动化运维架构视频教程

    课程目录L001-老男孩架构15期-Web架构之单机时代L002-老男孩架构15期-Web架构之集群时代L003-老男孩架构15期-Web架构之dnsL004-老男孩架构15期-Web架构之缓存体系L ...

  5. 后台管理遇到的坑一、style中css样式怎么传入变量值

    第一.给标签定义style变量 第二.在data中定义 第三.在methods中的方法中给样式赋值

  6. Python线性优化基础讲解~

    目前,各组织正在利用数据科学和机器学习来解决各种业务问题.为了创造一个真正的业务影响,如何弥合数据科学管道和业务决策管道之间的差距显得尤为重要. 数据科学管道的结果往往是数据中的预测.模式和洞察(通常 ...

  7. IOS手动添加的View 在代码中使用(自动布局)autoLayout

    - (void)viewDidLoad { [super viewDidLoad]; UIButton *btnTest = [UIButton buttonWithType:UIButtonType ...

  8. hadoop HDFS扩容

    1.纵向扩容(添加硬盘) 1.1 添加硬盘 确定完成添加,运行 lsblk 查看硬盘使用情况 1.2 硬盘分区 fdisk /dev/sdb #对新硬盘sdb进行分区 m 帮助 n 添加一个分区 p ...

  9. 智慧港口——基于二三维一体化GIS的港口可视化监管平台

    “智慧港口”是以现代化基础设施设备为基础,以云计算.大数据.物联网.移动互联网.智能控制等新一代信息技术与港口运输业务深度融合为核心,以港口运输组织服务创新为动力,以完善的体制机制.法律法规.标准规范 ...

  10. nes 红白机模拟器 第5篇 全屏显示

    先看一下效果图 放大的原理是使用最初级的算法,直接取对应像素法. /*================================================================= ...