我的个人博客排版更舒服: 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. js面试-手写代码实现new操作符的功能

    我们要搞清楚new操作符到底做了一些什么事情? 1.创建一个新的对象 2.将构造函数的作用域赋给新对象(因此this指向了这个新对象) 3.执行构造函数中的代码(为这个新对象添加属性) 4.返回新对象 ...

  2. Codeforces Round #620 (Div. 2)

    Codeforces Round #620 (Div. 2) A. Two Rabbits 题意 两只兔子相向而跳,一只一次跳距离a,另一只一次跳距离b,每次同时跳,问是否可能到同一位置 题解 每次跳 ...

  3. 必备技能一、webpack

    https://cloud.tencent.com/developer/section/1477376----->配置很重要 一.基本安装 mkdir webpack-demo &&am ...

  4. webpack的loader和plugin的区别

    [Loader]:用于对模块源码的转换,loader描述了webpack如何处理非javascript模块,并且在buld中引入这些依赖.loader可以将文件从不同的语言(如TypeScript)转 ...

  5. JS的类

    JS在创建之初不支持类,因为很多开发者为处理类创建了好多代码库,最终导致ES6引入了类. ES5及更早的版本都不支持类,与类最接近的是:创建一个构造器,然后将方法指派到该构造器的原型上.就是原型继承. ...

  6. 第四篇(1):企业常用Linux web环境安装配置(apache、php、mysql)

    上篇我们讲了基本的软件包管理和文件操作什么的,现在也要动手安装点有用的东西了吧! 本篇我会写出一个用yum安装apache.php.mysql的方法,最后再运行phpMyAdmin来管理数据库. 1. ...

  7. 逐浪web无障碍与国际化以及全民族语言支持白皮书

    北京时间2019年5月10日,领先的门户网站与WEB内核服务厂商--上海Zoomla!逐浪CMS团队发布其年度重榜产品:逐浪CMS全民族语言与国际版,体验站点:http://demo2.z01.com ...

  8. 《Java8 Stream编码实战》正式推出

    ​当我第一次在项目代码中看到Stream流的时候,心里不由得骂了一句"傻X"炫什么技.当我开始尝试在代码中使用Stream时,不由得感叹真香. 记得以前有朋友聊天说,他在代码中用了 ...

  9. java idea spring mvc 入门 最起码 我8080跑起来了

    IDEA建立Spring MVC Hello World 详细入门教程 https://www.cnblogs.com/wormday/p/8435617.html

  10. LSTM + linear-CRF序列标注笔记

    CRF 许多随机变量组成一个无向图G = {V, E},V代表顶点,E代表顶点间相连的边, 每个顶点代表一个随机变量,边代表两个随机变量间存在相互影响关系(变量非独立), 如果随机变量根据图的结构而具 ...