백엔드 개발 파이썬 튜토리얼 介绍Python中的一些高级编程技巧

介绍Python中的一些高级编程技巧

Jun 10, 2016 pm 03:16 PM
python

 正文:

本文展示一些高级的Python设计结构和它们的使用方法。在日常工作中,你可以根据需要选择合适的数据结构,例如对快速查找性的要求、对数据一致性的要求或是对索引的要求等,同时也可以将各种数据结构合适地结合在一起,从而生成具有逻辑性并易于理解的数据模型。Python的数据结构从句法上来看非常直观,并且提供了大量的可选操作。这篇指南尝试将大部分常用的数据结构知识放到一起,并且提供对其最佳用法的探讨。
推导式(Comprehensions)

如果你已经使用了很长时间的Python,那么你至少应该听说过列表推导(list comprehensions)。这是一种将for循环、if表达式以及赋值语句放到单一语句中的一种方法。换句话说,你能够通过一个表达式对一个列表做映射或过滤操作。

一个列表推导式包含以下几个部分:

  •     一个输入序列
  •     一个表示输入序列成员的变量
  •     一个可选的断言表达式
  •     一个将输入序列中满足断言表达式的成员变换成输出列表成员的输出表达式

举个例子,我们需要从一个输入列表中将所有大于0的整数平方生成一个新的序列,你也许会这么写:
 

num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = []
 
for number in num:
 if number > 0:
 filtered_and_squared.append(number ** 2)
print filtered_and_squared
 
# [1, 16, 100, 4, 9]
로그인 후 복사

很简单是吧?但是这就会有4行代码,两层嵌套外加一个完全不必要的append操作。而如果使用filter、lambda和map函数,则能够将代码大大简化:

num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = map(lambda x: x ** 2, filter(lambda x: x > 0, num))
print filtered_and_squared
 
# [1, 16, 100, 4, 9]
로그인 후 복사

嗯,这么一来代码就会在水平方向上展开。那么是否能够继续简化代码呢?列表推导能够给我们答案:

num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = [ x**2 for x in num if x > 0]
print filtered_and_squared
 
# [1, 16, 100, 4, 9]
로그인 후 복사

  • 迭代器(iterator)遍历输入序列num的每个成员x
  • 断言式判断每个成员是否大于零
  • 如果成员大于零,则被交给输出表达式,平方之后成为输出列表的成员。

列表推导式被封装在一个列表中,所以很明显它能够立即生成一个新列表。这里只有一个type函数调用而没有隐式调用lambda函数,列表推导式正是使用了一个常规的迭代器、一个表达式和一个if表达式来控制可选的参数。

另一方面,列表推导也可能会有一些负面效应,那就是整个列表必须一次性加载于内存之中,这对上面举的例子而言不是问题,甚至扩大若干倍之后也都不是问题。但是总会达到极限,内存总会被用完。

针对上面的问题,生成器(Generator)能够很好的解决。生成器表达式不会一次将整个列表加载到内存之中,而是生成一个生成器对象(Generator objector),所以一次只加载一个列表元素。

生成器表达式同列表推导式有着几乎相同的语法结构,区别在于生成器表达式是被圆括号包围,而不是方括号:

num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = ( x**2 for x in num if x > 0 )
print filtered_and_squared
 
# <generator object <genexpr> at 0x00583E18>
 
for item in filtered_and_squared:
 print item
 
# 1, 16, 100 4,9
로그인 후 복사

这比列表推导效率稍微提高一些,让我们再一次改造一下代码:

num = [1, 4, -5, 10, -7, 2, 3, -1]
 
def square_generator(optional_parameter):
 return (x ** 2 for x in num if x > optional_parameter)
 
print square_generator(0)
# <generator object <genexpr> at 0x004E6418>
 
# Option I
for k in square_generator(0):
 print k
# 1, 16, 100, 4, 9
 
# Option II
g = list(square_generator(0))
print g
# [1, 16, 100, 4, 9]

로그인 후 복사

除非特殊的原因,应该经常在代码中使用生成器表达式。但除非是面对非常大的列表,否则是不会看出明显区别的。

下例使用zip()函数一次处理两个或多个列表中的元素:

alist = ['a1', 'a2', 'a3']
blist = ['1', '2', '3']
 
for a, b in zip(alist, blist):
 print a, b
 
# a1 1
# a2 2
# a3 3
로그인 후 복사

再来看一个通过两阶列表推导式遍历目录的例子:

import os
def tree(top):
 for path, names, fnames in os.walk(top):
 for fname in fnames:
  yield os.path.join(path, fname)
 
for name in tree('C:\Users\XXX\Downloads\Test'):
 print name
로그인 후 복사

装饰器(Decorators)

装饰器为我们提供了一个增加已有函数或类的功能的有效方法。听起来是不是很像Java中的面向切面编程(Aspect-Oriented Programming)概念?两者都很简单,并且装饰器有着更为强大的功能。举个例子,假定你希望在一个函数的入口和退出点做一些特别的操作(比如一些安全、追踪以及锁定等操作)就可以使用装饰器。

装饰器是一个包装了另一个函数的特殊函数:主函数被调用,并且其返回值将会被传给装饰器,接下来装饰器将返回一个包装了主函数的替代函数,程序的其他部分看到的将是这个包装函数。

def timethis(func):
 '''
 Decorator that reports the execution time.
 '''
 pass
 
@timethis
def countdown(n):
 while n > 0:
 n -= 1
로그인 후 복사

语法糖@标识了装饰器。

好了,让我们回到刚才的例子。我们将用装饰器做一些更典型的操作:

import time
from functools import wraps
 
def timethis(func):
 '''
 Decorator that reports the execution time.
 '''
 @wraps(func)
 def wrapper(*args, **kwargs):
 start = time.time()
 result = func(*args, **kwargs)
 end = time.time()
 print(func.__name__, end-start)
 return result
 return wrapper
 
@timethis
def countdown(n):
 while n > 0:
 n -= 1
 
countdown(100000)
 
# ('countdown', 0.006999969482421875)

로그인 후 복사

当你写下如下代码时:

@timethis
def countdown(n):
로그인 후 복사

意味着你分开执行了以下步骤:

def countdown(n):
...
countdown = timethis(countdown)
로그인 후 복사

装饰器函数中的代码创建了一个新的函数(正如此例中的wrapper函数),它用 *args 和 **kwargs 接收任意的输入参数,并且在此函数内调用原函数并且返回其结果。你可以根据自己的需要放置任何额外的代码(例如本例中的计时操作),新创建的包装函数将作为结果返回并取代原函数。

@decorator
def function():
 print("inside function")
로그인 후 복사

当编译器查看以上代码时,function()函数将会被编译,并且函数返回对象将会被传给装饰器代码,装饰器将会在做完相关操作之后用一个新的函数对象代替原函数。

装饰器代码是什么样的?大部分的例子都是将装饰器定义为函数,而我发觉将装饰器定义成类更容易理解其功能,并且这样更能发挥装饰器机制的威力。

对装饰器的类实现唯一要求是它必须能如函数一般使用,也就是说它必须是可调用的。所以,如果想这么做这个类必须实现__call__方法。

这样的装饰器应该用来做些什么?它可以做任何事,但通常它用在当你想在一些特殊的地方使用原函数时,但这不是必须的,例如:

class decorator(object):
 
 def __init__(self, f):
 print("inside decorator.__init__()")
 f() # Prove that function definition has completed
 
 def __call__(self):
 print("inside decorator.__call__()")
 
@decorator
def function():
 print("inside function()")
 
print("Finished decorating function()")
 
function()
 
# inside decorator.__init__()
# inside function()
# Finished decorating function()
# inside decorator.__call__()

로그인 후 복사

译者注:
1. 语法糖@decorator相当于function=decorator(function),在此调用decorator的__init__打印“inside decorator.__init__()”
2. 随后执行f()打印“inside function()”
3. 随后执行“print(“Finished decorating function()”)”
4. 最后在调用function函数时,由于使用装饰器包装,因此执行decorator的__call__打印 “inside decorator.__call__()”。

一个更实际的例子:

def decorator(func):
 def modify(*args, **kwargs):
 variable = kwargs.pop('variable', None)
 print variable
 x,y=func(*args, **kwargs)
 return x,y
 return modify
 
@decorator
def func(a,b):
 print a**2,b**2
 return a**2,b**2
 
func(a=4, b=5, variable="hi")
func(a=4, b=5)
 
# hi
# 16 25
# None
# 16 25
로그인 후 복사

上下文管理库(ContextLib)

contextlib模块包含了与上下文管理器和with声明相关的工具。通常如果你想写一个上下文管理器,则你需要定义一个类包含__enter__方法以及__exit__方法,例如:

import time
class demo:
 def __init__(self, label):
 self.label = label
 
 def __enter__(self):
 self.start = time.time()
 
 def __exit__(self, exc_ty, exc_val, exc_tb):
 end = time.time()
 print('{}: {}'.format(self.label, end - self.start))
로그인 후 복사

完整的例子在此:

import time
 
class demo:
 def __init__(self, label):
 self.label = label
 
 def __enter__(self):
 self.start = time.time()
 
 def __exit__(self, exc_ty, exc_val, exc_tb):
 end = time.time()
 print('{}: {}'.format(self.label, end - self.start))
 
with demo('counting'):
 n = 10000000
 while n > 0:
 n -= 1
 
# counting: 1.36000013351

로그인 후 복사

上下文管理器被with声明所激活,这个API涉及到两个方法。
1. __enter__方法,当执行流进入with代码块时,__enter__方法将执行。并且它将返回一个可供上下文使用的对象。
2. 当执行流离开with代码块时,__exit__方法被调用,它将清理被使用的资源。

利用@contextmanager装饰器改写上面那个例子:

from contextlib import contextmanager
import time
 
@contextmanager
def demo(label):
 start = time.time()
 try:
 yield
 finally:
 end = time.time()
 print('{}: {}'.format(label, end - start))
 
with demo('counting'):
 n = 10000000
 while n > 0:
 n -= 1
 
# counting: 1.32399988174

로그인 후 복사

看上面这个例子,函数中yield之前的所有代码都类似于上下文管理器中__enter__方法的内容。而yield之后的所有代码都如__exit__方法的内容。如果执行过程中发生了异常,则会在yield语句触发。
描述器(Descriptors)

描述器决定了对象属性是如何被访问的。描述器的作用是定制当你想引用一个属性时所发生的操作。

构建描述器的方法是至少定义以下三个方法中的一个。需要注意,下文中的instance是包含被访问属性的对象实例,而owner则是被描述器修辞的类。

__get__(self, instance, owner) – 这个方法是当属性被通过(value = obj.attr)的方式获取时调用,这个方法的返回值将被赋给请求此属性值的代码部分。
__set__(self, instance, value) – 这个方法是当希望设置属性的值(obj.attr = ‘value')时被调用,该方法不会返回任何值。
__delete__(self, instance) – 当从一个对象中删除一个属性时(del obj.attr),调用此方法。

译者注:对于instance和owner的理解,考虑以下代码:

class Celsius(object):
 def __init__(self, value=0.0):
 self.value = float(value)
 def __get__(self, instance, owner):
 return self.value
 def __set__(self, instance, value):
 self.value = float(value)
 
class Temperature(object):
 celsius = Celsius()
 
temp=Temperature()
temp.celsius #calls Celsius.__get__
로그인 후 복사

上例中,instance指的是temp,而owner则是Temperature。

LazyLoading Properties例子:

import weakref
 
class lazyattribute(object):
 def __init__(self, f):
 self.data = weakref.WeakKeyDictionary()
 self.f = f
 def __get__(self, obj, cls):
 if obj not in self.data:
  self.data[obj] = self.f(obj)
 return self.data[obj]
 
class Foo(object):
 @lazyattribute
 def bar(self):
 print "Being lazy"
 return 42
 
f = Foo()
 
print f.bar
# Being lazy
# 42
 
print f.bar
# 42
로그인 후 복사

描述器很好的总结了Python中的绑定方法(bound method)这个概念,绑定方法是经典类(classic classes)的实现核心。在经典类中,当在一个对象实例的字典中没有找到某个属性时,会继续到类的字典中查找,然后再到基类的字典中,就这么一直递归的查找下去。如果在类字典中找到这个属性,解释器会检查找到的对象是不是一个Python函数对象。如果是,则返回的并不是这个对象本身,而是返回一个柯里化(currying function)的包装器对象。当调用这个包装器时,它会首先在参数列表之前插入实例,然后再调用原函数。

译者注:
1. 柯里化 – http://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96
2. function,method,bound method及unbound method的区别。首先,函数(function)是由def或lambda创建的。当一个函数在class语句块中定义或是由type来创建时,它会转成一个非绑定方法(unbound method),而当通过类实例(instance)来访问此方法的时候,它将转成绑定方法(bound method),绑定方法会自动将实例作为第一个参数传入方法。综上所述,方法是出现在类中的函数,绑定方法是一个绑定了具体实例的方法,反之则是非绑定方法。

综上,描述器被赋值给类,而这些特殊的方法就在属性被访问的时候根据具体的访问类型自动地调用。
元类(MetaClasses)

元类提供了一个改变Python类行为的有效方式。

元类的定义是“一个类的类”。任何实例是它自己的类都是元类。

class demo(object):
 pass
 
obj = demo()
 
print "Class of obj is {0}".format(obj.__class__)
print "Class of obj is {0}".format(demo.__class__)
 
# Class of obj is <class '__main__.demo'>
# Class of obj is <type 'type'>
로그인 후 복사

在上例中,我们定义了一个类demo,并且生成了一个该类的对象obj。首先,可以看到obj的__class__是demo。有意思的来了,那么demo的class又是什么呢?可以看到demo的__class__是type。

所以说type是python类的类,换句话说,上例中的obj是一个demo的对象,而demo本身又是type的一个对象。

所以说type就是一个元类,而且是python中最常见的元类,因为它使python中所有类的默认元类。

因为元类是类的类,所以它被用来创建类(正如类是被用来创建对象的一样)。但是,难道我们不是通过一个标准的类定义来创建类的么?的确是这样,但是python内部的运作机制如下:

  •         当看见一个类定义,python会收集所有属性到一个字典中。
  •         当类定义结束,python将决定类的元类,我们就称它为Meta吧。
  •         最后,python执行Meta(name, bases, dct),其中:

a. Meta是元类,所以这个调用是实例化它。
b. name是新建类的类名。
c. bases是新建类的基类元组
d. dct将属性名映射到对象,列出所有的类属性。

那么如何确定一个类(A)的元类呢?简单来说,如果一个类(A)自身或其基类(Base_A)之一有__metaclass__属性存在,则这个类(A/Base_A)就是类(A)的元类。否则type就将是类(A)的元类。
模式(Patterns)

“请求宽恕比请求许可更容易(EFAP)”

这个Python设计原则是这么说的“请求宽恕比请求许可更容易(EFAP)”。不提倡深思熟虑的设计思路,这个原则是说应该尽量去尝试,如果遇到错误,则给予妥善的处理。Python有着强大的异常处理机制可以支持这种尝试,这些机制帮助程序员开发出更为稳定,容错性更高的程序。

单例

单例是指只能同时存在一个的实例对象。Python提供了很多方法来实现单例。

Null对象

Null对象能够用来代替None类型以避免对None的测试。

观察者

观察者模式允许多个对象访问同一份数据。

构造函数

构造函数的参数经常被赋值给实例的变量。这种模式能够用一行代码替代多个手动赋值语句。
总结

谢谢阅读,如有疑问,请留言讨论。

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

PHP와 Python : 다른 패러다임이 설명되었습니다 PHP와 Python : 다른 패러다임이 설명되었습니다 Apr 18, 2025 am 12:26 AM

PHP는 주로 절차 적 프로그래밍이지만 객체 지향 프로그래밍 (OOP)도 지원합니다. Python은 OOP, 기능 및 절차 프로그래밍을 포함한 다양한 패러다임을 지원합니다. PHP는 웹 개발에 적합하며 Python은 데이터 분석 및 기계 학습과 같은 다양한 응용 프로그램에 적합합니다.

PHP와 Python 중에서 선택 : 가이드 PHP와 Python 중에서 선택 : 가이드 Apr 18, 2025 am 12:24 AM

PHP는 웹 개발 및 빠른 프로토 타이핑에 적합하며 Python은 데이터 과학 및 기계 학습에 적합합니다. 1.PHP는 간단한 구문과 함께 동적 웹 개발에 사용되며 빠른 개발에 적합합니다. 2. Python은 간결한 구문을 가지고 있으며 여러 분야에 적합하며 강력한 라이브러리 생태계가 있습니다.

PHP와 Python : 그들의 역사에 깊은 다이빙 PHP와 Python : 그들의 역사에 깊은 다이빙 Apr 18, 2025 am 12:25 AM

PHP는 1994 년에 시작되었으며 Rasmuslerdorf에 의해 개발되었습니다. 원래 웹 사이트 방문자를 추적하는 데 사용되었으며 점차 서버 측 스크립팅 언어로 진화했으며 웹 개발에 널리 사용되었습니다. Python은 1980 년대 후반 Guidovan Rossum에 의해 개발되었으며 1991 년에 처음 출시되었습니다. 코드 가독성과 단순성을 강조하며 과학 컴퓨팅, 데이터 분석 및 기타 분야에 적합합니다.

Python vs. JavaScript : 학습 곡선 및 사용 편의성 Python vs. JavaScript : 학습 곡선 및 사용 편의성 Apr 16, 2025 am 12:12 AM

Python은 부드러운 학습 곡선과 간결한 구문으로 초보자에게 더 적합합니다. JavaScript는 가파른 학습 곡선과 유연한 구문으로 프론트 엔드 개발에 적합합니다. 1. Python Syntax는 직관적이며 데이터 과학 및 백엔드 개발에 적합합니다. 2. JavaScript는 유연하며 프론트 엔드 및 서버 측 프로그래밍에서 널리 사용됩니다.

숭고한 코드 파이썬을 실행하는 방법 숭고한 코드 파이썬을 실행하는 방법 Apr 16, 2025 am 08:48 AM

Sublime 텍스트로 Python 코드를 실행하려면 먼저 Python 플러그인을 설치 한 다음 .py 파일을 작성하고 코드를 작성한 다음 CTRL B를 눌러 코드를 실행하면 콘솔에 출력이 표시됩니다.

Windows 8에서 코드를 실행할 수 있습니다 Windows 8에서 코드를 실행할 수 있습니다 Apr 15, 2025 pm 07:24 PM

VS 코드는 Windows 8에서 실행될 수 있지만 경험은 크지 않을 수 있습니다. 먼저 시스템이 최신 패치로 업데이트되었는지 확인한 다음 시스템 아키텍처와 일치하는 VS 코드 설치 패키지를 다운로드하여 프롬프트대로 설치하십시오. 설치 후 일부 확장은 Windows 8과 호환되지 않을 수 있으며 대체 확장을 찾거나 가상 시스템에서 새로운 Windows 시스템을 사용해야합니다. 필요한 연장을 설치하여 제대로 작동하는지 확인하십시오. Windows 8에서는 VS 코드가 가능하지만 더 나은 개발 경험과 보안을 위해 새로운 Windows 시스템으로 업그레이드하는 것이 좋습니다.

vscode에서 코드를 작성하는 위치 vscode에서 코드를 작성하는 위치 Apr 15, 2025 pm 09:54 PM

Visual Studio Code (VSCODE)에서 코드를 작성하는 것은 간단하고 사용하기 쉽습니다. vscode를 설치하고, 프로젝트를 만들고, 언어를 선택하고, 파일을 만들고, 코드를 작성하고, 저장하고 실행합니다. VSCODE의 장점에는 크로스 플랫폼, 무료 및 오픈 소스, 강력한 기능, 풍부한 확장 및 경량 및 빠른가 포함됩니다.

메모장으로 파이썬을 실행하는 방법 메모장으로 파이썬을 실행하는 방법 Apr 16, 2025 pm 07:33 PM

메모장에서 Python 코드를 실행하려면 Python 실행 파일 및 NPPEXEC 플러그인을 설치해야합니다. Python을 설치하고 경로를 추가 한 후 nppexec 플러그인의 명령 "Python"및 매개 변수 "{current_directory} {file_name}"을 구성하여 Notepad의 단축키 "F6"을 통해 Python 코드를 실행하십시오.

See all articles