Python 是一种通用编程语言,因其可读性、面向对象的特性和强大的社区支持而广受欢迎。除了用于 Web 应用程序之外,Python 还用于数据科学、人工智能、科学计算等各个领域。因此,如果你一直在考虑进入编程领域并寻找一种通用语言,那么 Python 可能适合你。在本文中将分享一些高级 Python 概念,这些概念将帮助你在扎根的同时取得成功。阅读本文可以不必是经验丰富的 Python 程序员;它只会帮助你更好地理解语言并使你成为更好的开发人员。
推导式 推导式分为三种类型:列表推导、字典推导和集合推导。如果你想从现有的可迭代对象中创建新的列表、字典或集合,请使用推导式。以下代码片段展示了这些用法
复制 # 创建一个用于推导式的列表
numbers = [1 , 2 , 3 , - 3 , - 2 , - 1 ]
# 创建一个包含这些数字的方格的新列表
mylist = [x * x for x in numbers ][1 , 4 , 9 , 9 , 4 , 1 ]
# 为这些数字的幂创建一个新字典
mydict = {x : pow (10 , x ) for x in numbers }
# 输出 {1: 10, 2: 100, 3: 1000, -3: 0.001, -2: 0.01, -1: 0.1}
# 创建一组这些数字的绝对值
myset = {abs (x ) for x in numbers }
# 输出 {1, 2, 3}
这些推导具有相似的语法。以下是对不同形式的简要概述。值得注意的是可以设置条件以确保保留所需的元素
复制 列表推导: [expr for x in iterable ]
字典推导: {key_expr : value_expr for x in iterable }
集合推导: {expr for x in iterable }
可选条件:
[expr for x in iterable if condition ]
{key_expr : value_expr for x in iterable if condition }
{expr for x in iterable if condition }
异常处理
异常是在程序执行期间出现并导致程序中断的情况。它可能由各种原因而发生。假设正在构建一个除法程序,并且分母包含 0,从而导致 ZeroDivisionError。导入不存在的库或访问不在列表索引中的元素是另外两个实例。Python 带有大约 30 个内置异常。 try和except块用于处理python中的异常。except当我们需要同时处理多个异常时,我们可以使用多个块。try块是要执行的指令。except块包含执行try失败时执行的代码。还有else和finally块。else块仅在try成功执行块时执行。finally无论前一个块的结果如何,finally块将始终执行。
复制 try :
a = int (input ("Enter numerator:" )
b = int (input ("Enter denominator:" )
c = a / b
print (c )
except ZeroDivisionError as e :
print (e , " please provide a non zero denominator" )
复制 import sys
try :
f = open ('myfile.txt' )
s = f .readline ()
i = int (s .strip ())
except OSError as err :
print ("OS error: {0}" .format (err ))
except ValueError :
print ("Could not convert data to an integer." )
except :
print ("Unexpected error:" , sys .exc_info ()[0 ])
raise
finally :
print ("Operation Successfully Done!!" )(Example Taken From Official Python Docs )
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 集合库
将可迭代对象作为输入并返回一个字典,其中键是可迭代元素,值是它们各自在原始可迭代对象中出现的次数。
复制 from collections import Counter
data = [1 ,1 ,1 ,1 ,2 ,3 ,4 ,3 ,3 ,5 ,6 ,7 ,7 ]
count = Counter (data )
print (count )
## Counter({1: 4, 3: 3, 7: 2, 2: 1, 4: 1, 5: 1, 6: 1})
生成可以使用名字来访问元素内容的tuple子类,命名元组赋予每个位置一个含义,提供可读性和自文档性。
复制 from collections import namedtuple
Direction = namedtuple ('Direction' ,'N,S,E,W' )
dt = Direction (4 ,74 ,0 ,0 )
print (dt )
# Direction(N=4, S=74, E=0, W=0)
这是一个记忆键插入顺序的字典结构。最新版本的 python 中字典已经包含了这个特性。
复制 from collections import OrderedDict
dictt = OrderedDict ()
dictt ['a' ] = 5
dictt ['d' ] = 2
dictt ['c' ] = 1
dictt ['b' ] = 3
print (dictt )
# OrderedDict([('a', 5), ('d', 2), ('c', 1), ('b', 3)])
这是一个字典结构,当访问结构中不存在的键时将返回默认值,而不是引发错误。
复制 from collections import defaultdict
dictt = defaultdict (int )
dictt ['a' ] = 2
print (dictt ['a' ]) ##返回值
print (dictt ['b' ]) ##返回默认值
# 2
# 0
双端队列,可以快速的从另外一侧追加和推出对象,deque是一个双向链表,针对list连续的数据结构插入和删除进行优化。它提供了两端都可以操作的序列,这表示在序列的前后你都可以执行添加或删除操作。
复制 from collections import deque
d = deque ('abc' )
d .append ('d' )
print (d )
# deque(['a', 'b', 'c', 'd'])
# appendleft 添加元素到左端
d .appendleft ('e' )
print (d )
# deque(['e','a', 'b', 'c', 'd'])
# clear 清除所有元素
d .clear ()
print (d )
# deque([])
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 还有一些经常用的方法比如pop,copy,count,extend,index,insert,popleft,remove,reverse,maxlen
迭代工具
Python itertools 模块提供了适用于迭代器的各种函数。
product(iterable,iterable) 两个迭代的笛卡尔积。 permutation(iterable) 没有重复元素的所有可能排列。 combinations(iterable,n) 来自可迭代的 n 个元素的所有可能组合,无需替换。 combinations_with_replacement(iterable,n) 来自可迭代的 n 个元素的所有可能组合与替换。 accumulate(iterable) 返回可迭代的元素的累积和。 groupby(iterable, key=FUNC) 从可迭代对象中返回具有连续键和组的迭代器。 装饰器 装饰器是 Python 中修改函数和类行为的一种方式。它们允许你通过添加方法或更改参数来更改功能,或通过添加属性来更改类。
例如,如果想在每次调用“my_function”函数时记录日志,可以这样编写代码:
复制 def logging_func (original_func ):
def wrapper (* args , * * kwargs ):
print (f"Called {original_func .__name__ } with" , args , kwargs )
return original_func (* args , * * kwargs )
return wrapper
@ logging_func
def add (a , b ):
return a + b
result = add (5 , 6 )
print (result )
让我们解释上面的装饰器的例子——首先,我们有一个函数名add,它的工作是获取两个变量并返回它们的总和。现在经过一段时间的工作,我们意识到需要将功能记录到相同的函数中。现在我们有两个选择,第一个是在同一个add函数中添加函数调用日志代码,或者我们可以使用装饰器添加功能而不显式更改函数。为了使用装饰器,我们首先定义了一个装饰器函数。该函数original_func作为输入。然后,我们有另一个功能。它是一个具有*args, **kwargs函数参数的包装函数。有了这些,现在都定义为参数,我们可以在函数内传递任意数量的参数。在包装函数的主体中,我们有日志功能的逻辑。当我们add使用一些参数调用函数时add(5,6),输出将是:
生成器 生成器是一个返回可迭代值序列的函数。与一次返回所有元素并消耗整个列表长度的内存的列表不同,生成器会一个一个地生成项目。它至少包含一个yield声明。yield是python中的一个关键字,用于从函数返回值而不破坏其当前状态或对局部变量的引用。带有yield关键字的函数称为生成器。
比如最经典的面试题斐波那契数列
复制 def fibon (limit ):
a ,b = 0 ,1
while a < limit :
yield a
a , b = b , a + bfor x in fibon (10 ):
print (x )
魔术方法 Magic方法,__方法名前后有两个下划线。在某个动作上,这些方法直接从类中调用。当使用*a 符号将两个数字相乘时,将__mul__调用内部过程。
复制 num = 5
num * 6
>> 30
num .__mul__ (6 )
>> 30
通常,这些方法用于重载预定义的运算符。例如,数字运算符+,-,*,/必须在数字对象周围使用,但+也可以用于连接两个字符串。因此,我们可能会争辩说+号操作符在执行字符串连接消耗的内存大。
复制 5 + 6
>> 11
"python" + "programming"
>> 'pythonprogramming'
哈希性 在学习 Python 字典时,我们了解到键必须是可散列的。可哈希是什么意思?Hashable 基本上表示一个 Python 对象可以被散列,也就是散列的行为。下图描述了散列的工作流程。
散列是使用散列函数(也称为散列器)(在图中称为散列)将 Python 对象(在图中称为键)转换为数字散列值的过程。使用内置的 hash() 方法来获取 Python 对象的哈希值是一种判断它是否存在的简单方法。如果对象不可散列,Python 将抛出 TypeError 异常。
复制 # Get an string object’s hash value
hash ("This is me" )
5361907397716593195
# Get a tuple object’s hash value
hash ((1 ,2 ))
- 3550055125485641917
# Get a list object’s hash value
hash ([1 , 2 , 3 ])
Traceback (most recent call last ):
File “ < stdin > ” , line 1 , in < module >
TypeError : unhashable type : ‘list’
# Get a dict object’s hash value
hash ({“a” : 1 , “b” : 2 })
Traceback (most recent call last ):
File “ < stdin > ” , line 1 , in < module >
TypeError : unhashable type : ‘dict’
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 特别是散列需要时间,并且比构造列表和元组要慢。
那么,为什么我们首先要费心使用散列创建字典呢?
在类似的问题上,你可能听说过设置项也必须是可散列的。字典和集合都需要在底层创建哈希表。以下代码片段演示了特定对象的哈希性如何影响它们作为字典键的适用性。哈希最显着的好处是它们在检索字典元素时提供即时查找时间(即 O(1) 时间复杂度)。检查特定项目是否在集合中需要相同的时间。换句话说,使用散列作为实现机制减少了在后台使用散列表的开销,同时提高了一些常见操作(如项目检索、项目插入和项目验证)的效率。
复制 import random
import timeit
# Create a function to check the look up time
def dict_look_up_time(n):
numbers = list(range(n))
random.shuffle(numbers)
d0 = {x: str(x) for x in numbers}
random_int = random.randint(0, n — 1)
t0 = timeit.timeit(lambda: d0[random_int], number=10000)
return t0
for n in (10, 100, 1000, 10000, 100000):
elapse_time = dict_look_up_time(n)
print(f”*** N = {n:<8}: {elapse_time:.5f}”)
*** N = 10 : 0.00114
*** N = 100 : 0.00256
*** N = 1000 : 0.00291
*** N = 10000 : 0.00207
*** N = 100000 : 0.00286
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 上面的代码生成一些随机整数来确定项目获取的平均查找时间,来模拟真实情况。如你所见,即使字典中有 100,000 个条目,查找时间也几乎相同,这证明了使用哈希表作为字典存储机制的好处。
本文转载自微信公众号「树哥会编程」,可以通过以下二维码关注。转载本文请联系树哥会编程公众号。