Python简明教程在线阅读地址: https://bop.molun.net/
DocStrings
该文档字符串所约定的是一串多行字符串,其中第一行以某一大写字母开始,以句号结束。第二行为空行,后跟的第三行开始是任何详细的解释说明。 在此强烈建议你在有关你所有非凡功能的文档字符串中都遵循这一约定。我们可以通过使用函数的 __doc__(注意其中的双下划綫)属性(属于函数的名称)来获取函数 print_max 的文档字符串属性。 只消记住 Python 将所有东西都视为一个对象,这其中自然包括函数。我们将在后面的类(Class)章节讨论有关对象的更多细节。+如果你曾使用过 Python 的 help() 函数,那么你应该已经了解了文档字符串的用途了。它所做的便是获取函数的 __doc__ 属性并以一种整洁的方式将其呈现给你 。你可以在上方的函数中尝试一下——只需在程序中包含 help(print_max) 就行了。要记住你可以通过按下 q 键来退出 help。自动化工具可以以这种方式检索你的程序中的文档。因此,我强烈推荐你为你编写的所有不平凡的函数配以文档字符串。 你的 Python 发行版中附带的 pydoc 命令与 help() 使用文档字符串的方式类似。
print_max(3, 5)print(print_max.__doc__)
from math import sqrtprint("Square root of 16 is", sqrt(16))
因此,我们大都推荐最好去使用 import 语句,尽管这会使你的程序变得稍微长一些。你还可以使用:from mymodule import *这将导入诸如 say_hi 等所有公共名称,但不会导入 __version__ 名称,因为后者以双下划线开头。
dir 函数内置的 dir() 函数能够返回由对象所定义的名称列表。 如果这一对象是一个模块,则该列表会包括函数内所定义的函数、类与变量。
dir(sys)
列表:
shoplist = ['apple', 'mango', 'carrot', 'banana']print('I have', len(shoplist), 'items to purchase.')print('These items are:', end=' ')for item in shoplist: print(item, end=' ')print('\nI also have to buy rice.')shoplist.append('rice')print('My shopping list is now', shoplist)print('I will sort my list now')shoplist.sort()print('Sorted shopping list is', shoplist)print('The first item I will buy is', shoplist[0])olditem = shoplist[0]del shoplist[0]print('I bought the', olditem)print('My shopping list is now', shoplist)
元组(Tuple):
zoo = ('python', 'elephant', 'penguin')print('Number of animals in the zoo is', len(zoo))new_zoo = 'monkey', 'camel', zooprint('Number of cages in the new zoo is', len(new_zoo))print('All animals in new zoo are', new_zoo)print('Animals brought from old zoo are', new_zoo[2])print('Last animal brought from old zoo is', new_zoo[2][2])print('Number of animals in the new zoo is', len(new_zoo)-1+len(new_zoo[2]))
字典:
ab = { 'Swaroop': 'swaroop@swaroopch.com', 'Larry': 'larry@wall.org', 'Matsumoto': 'matz@ruby-lang.org', 'Spammer': 'spammer@hotmail.com'}print("Swaroop's address is", ab['Swaroop'])# 删除一对键值—值配对del ab['Spammer']print('\nThere are {} contacts in the address-book\n'.format(len(ab)))for name, address in ab.items(): print('Contact {} at {}'.format(name, address))# 添加一对键值—值配对ab['Guido'] = 'guido@python.org'if 'Guido' in ab: print("\nGuido's address is", ab['Guido'])
shoplist = ['apple', 'mango', 'carrot', 'banana']name = 'swaroop'# Indexing or 'Subscription' operation ## 索引或“下标(Subcription)”操作符 #print('Item 0 is', shoplist[0])print('Item 1 is', shoplist[1])print('Item 2 is', shoplist[2])print('Item 3 is', shoplist[3])print('Item -1 is', shoplist[-1])print('Item -2 is', shoplist[-2])print('Character 0 is', name[0])# Slicing on a list #print('Item 1 to 3 is', shoplist[1:3])print('Item 2 to end is', shoplist[2:])print('Item 1 to -1 is', shoplist[1:-1])print('Item start to end is', shoplist[:])# 从某一字符串中切片 #print('characters 1 to 3 is', name[1:3])print('characters 2 to end is', name[2:])print('characters 1 to -1 is', name[1:-1])print('characters start to end is', name[:])
输出:
$ python ds_seq.pyItem 0 is appleItem 1 is mangoItem 2 is carrotItem 3 is bananaItem -1 is bananaItem -2 is carrotCharacter 0 is sItem 1 to 3 is ['mango', 'carrot']Item 2 to end is ['carrot', 'banana']Item 1 to -1 is ['mango', 'carrot']Item start to end is ['apple', 'mango', 'carrot', 'banana']characters 1 to 3 is wacharacters 2 to end is aroopcharacters 1 to -1 is waroocharacters start to end is swaroop
集合:
>>> bri = set(['brazil', 'russia', 'india'])>>> 'india' in briTrue>>> 'usa' in briFalse>>> bric = bri.copy()>>> bric.add('china')>>> bric.issuperset(bri)True>>> bri.remove('russia')>>> bri & bric # OR bri.intersection(bric){ 'brazil', 'india'}
print('Simple Assignment')shoplist = ['apple', 'mango', 'carrot', 'banana']# mylist 只是指向同一对象的另一种名称mylist = shoplist# 我购买了第一项项目,所以我将其从列表中删除del shoplist[0]print('shoplist is', shoplist)print('mylist is', mylist)# 注意到 shoplist 和 mylist 二者都# 打印出了其中都没有 apple 的同样的列表,以此我们确认# 它们指向的是同一个对象print('Copy by making a full slice')# 通过生成一份完整的切片制作一份列表的副本mylist = shoplist[:]# 删除第一个项目del mylist[0]print('shoplist is', shoplist)print('mylist is', mylist)# 注意到现在两份列表已出现不同
要记住列表的赋值语句不会创建一份副本。你必须使用切片操作来生成一份序列的副本。
字符串:
# 这是一个字符串对象name = 'Swaroop'if name.startswith('Swa'): print('Yes, the string starts with "Swa"')if 'a' in name: print('Yes, it contains the string "a"')if name.find('war') != -1: print('Yes, it contains the string "war"')delimiter = '_*_'mylist = ['Brazil', 'Russia', 'India', 'China']print(delimiter.join(mylist))输出:$ python ds_str_methods.pyYes, the string starts with "Swa"Yes, it contains the string "a"Yes, it contains the string "war"Brazil_*_Russia_*_India_*_China
同时你还要注意当一个对象变量与一个类变量名称相同时,类变量将会被隐藏。
除了 Robot.popluation
,我们还可以使用 self.__class__.population
,因为每个对象都通过 self.__class__
属性来引用它的类。
@classmethod def how_many(cls): """打印出当前的人口数量""" print("We have {:d} robots.".format(cls.population))
how_many 实际上是一个属于类而非属于对象的方法。这就意味着我们可以将它定义为一个 classmethod(类方法) 或是一个 staticmethod(静态方法),这取决于我们是否知道我们需不需要知道我们属于哪个类。 由于我们已经引用了一个类变量,因此我们使用 classmethod(类方法)。我们使用装饰器(Decorator)将 how_many 方法标记为类方法。你可以将装饰器想象为调用一个包装器(Wrapper)函数的快捷方式,因此启用 @classmethod 装饰器等价于调用:how_many = classmethod(how_many)
在本程序中,我们还会看见针对类和方法的 文档字符串(DocStrings) 的使用方式。我们可以在运行时通过 Robot.__doc__ 访问类的 文档字符串,对于方法的文档字符串,则可以使用 Robot.say_hi.__doc__。在 die 方法中,我们简单地将 Robot.population 的计数按 1 向下减少。所有的类成员都是公开的。但有一个例外:如果你使用数据成员并在其名字中使用双下划线作为前缀,形成诸如 __privatervar 这样的形式,Python 会使用名称调整(Name-mangling)来使其有效地成为一个私有变量。因此,你需要遵循这样的约定:任何在类或对象之中使用的变量其命名应以下划线开头,其它所有非此格式的名称都将是公开的,并可以为其它任何类或对象所使用。请记得这只是一个约定,Python 并不强制如此(除了双下划线前缀这点)。+针对 C++/Java/C# 程序员的提示所有类成员(包括数据成员)都是公开的,并且 Python 中所有的方法都是虚拟的(Vireual)。
要想使用继承,在定义类6时我们需要在类后面跟一个包含基类名称的元组。然后,我们会注意到基类的 __init__ 方法是通过 self 变量被显式调用的,因此我们可以初始化对象的基类部分。 下面这一点很重要,需要牢记——因为我们在 Teacher 和 Student 子类中定义了 __init__ 方法,Python 不会自动调用基类 SchoolMember 的构造函数,你必须自己显式地调用它。相反,如果我们没有在一个子类中定义一个 __init__ 方法,Python 将会自动调用基类的构造函数。
Pickle:
Python 提供了一个叫作 Pickle
的标准模块,通过它你可以将任何纯 Python 对象存储到一个文件中,并在稍后将其取回。这叫作持久地(Persistently)存储对象。
import pickle# The name of the file where we will store the objectshoplistfile = 'shoplist.data'# The list of things to buyshoplist = ['apple', 'mango', 'carrot']# Write to the filef = open(shoplistfile, 'wb')# Dump the object to a filepickle.dump(shoplist, f)f.close()# Destroy the shoplist variabledel shoplist# Read back from the storagef = open(shoplistfile, 'rb')# Load the object from the filestoredlist = pickle.load(f)print(storedlist)输出:$ python io_pickle.py['apple', 'mango', 'carrot']
异常处理:
# encoding=UTF-8class ShortInputException(Exception): '''一个由用户定义的异常类''' def __init__(self, length, atleast): Exception.__init__(self) self.length = length self.atleast = atleasttry: text = input('Enter something --> ') if len(text) < 3: raise ShortInputException(len(text), 3) # 其他工作能在此处继续正常运行except EOFError: print('Why did you do an EOF on me?')except ShortInputException as ex: print(('ShortInputException: The input was ' + '{0} long, expected at least {1}') .format(ex.length, ex.atleast))else: print('No exception was raised.')输出:$ python exceptions_raise.pyEnter something --> aShortInputException: The input was 1 long, expected at least 3$ python exceptions_raise.pyEnter something --> abcNo exception was raised
Try ... Finally
假设你正在你的读取中读取一份文件。你应该如何确保文件对象被正确关闭,无论是否会发生异常?这可以通过 finally
块来完成。
import sysimport timef = Nonetry: f = open("poem.txt") # 我们常用的文件阅读风格 while True: line = f.readline() if len(line) == 0: break print(line, end='') sys.stdout.flush() print("Press ctrl+c now") # 为了确保它能运行一段时间 time.sleep(2)except IOError: print("Could not find file poem.txt")except KeyboardInterrupt: print("!! You cancelled the reading from the file.")finally: if f: f.close() print("(Cleaning up: Closed the file)")输出:$ python exceptions_finally.pyProgramming is funPress ctrl+c now^C!! You cancelled the reading from the file.(Cleaning up: Closed the file)
with 语句:
在 try 块中获取资源,然后在 finally 块中释放资源是一种常见的模式。因此,还有一个 with 语句使得这一过程可以以一种干净的姿态得以完成。保存为 exceptions_using_with.py:with open("poem.txt") as f: for line in f: print(line, end='')它是如何工作的程序输出的内容应与上一个案例所呈现的相同。本例的不同之处在于我们使用的是 open 函数与 with 语句——我们将关闭文件的操作交由 with open 来自动完成。在幕后发生的事情是有一项 with 语句所使用的协议(Protocol)。它会获取由 open 语句返回的对象,在本案例中就是“thefile”。它总会在代码块开始之前调用 thefile.__enter__ 函数,并且总会在代码块执行完毕之后调用 thefile.__exit__。因此,我们在 finally 代码块中编写的代码应该格外留心 __exit__ 方法的自动操作。这能够帮助我们避免重复发显式使用 try..finally 语句。
传递元组
你可曾希望从一个函数中返回两个不同的值?你能做到的。只需要使用一个元组。
>>> def get_error_details():... return (2, 'details')...>>> errnum, errstr = get_error_details()>>> errnum2>>> errstr'details'要注意到 a, b =的用法会将表达式的结果解释为具有两个值的一个元组。这也意味着在 Python 中交换两个变量的最快方法是:>>> a = 5; b = 8>>> a, b(5, 8)>>> a, b = b, a>>> a, b(8, 5)
Lambda 表格
lambda
语句可以创建一个新的函数对象。从本质上说,lambda
需要一个参数,后跟一个表达式作为函数体,这一表达式执行的值将作为这个新函数的返回值。
案例(保存为 more_lambda.py
):
points = [{ 'x': 2, 'y': 3}, { 'x': 4, 'y': 1}] points.sort(key=lambda i: i['y']) print(points)
输出:
$ python more_lambda.py[{'y': 1, 'x': 4}, {'y': 3, 'x': 2}]
它是如何工作的
要注意到一个 list
的 sort
方法可以获得一个 key
参数,用以决定列表的排序方式(通常我们只知道升序与降序)。
在我们的案例中,我们希望进行一次自定义排序,为此我们需要编写一个函数,但是又不是为函数编写一个独立的 def
块,只在这一个地方使用,因此我们使用 Lambda 表达式来创建一个新函数。
列表推导
列表推导(List Comprehension)用于从一份现有的列表中得到一份新列表。想象一下,现在你已经有了一份数字列表,你想得到一个相应的列表,其中的数字在大于 2 的情况下将乘以 2。列表推导就是这类情况的理想选择。
案例(保存为 more_list_comprehension.py
):
listone = [2, 3, 4]listtwo = [2*i for i in listone if i > 2] print(listtwo)
输出:
$ python more_list_comprehension.py[6, 8]
它是如何工作的
在本案例中,当满足了某些条件时(if i > 2
),我们进行指定的操作(2*i
),以此来获得一份新的列表。要注意到原始列表依旧保持不变。
使用列表推导的优点在于,当我们使用循环来处理列表中的每个元素并将其存储到新的列表中时时,它能减少样板(Boilerplate)代码的数量。
在函数中接收元组与字典
有一种特殊方法,即分别使用 *
或 **
作为元组或字典的前缀,来使它们作为一个参数为函数所接收。当函数需要一个可变数量的实参时,这将颇为有用。
>>> def powersum(power, *args): ... '''Return the sum of each argument raised to the specified power.''' ... total = 0 ... for i in args: ... total += pow(i, power) ... return total ... >>> powersum(2, 3, 4) 25 >>> powersum(2, 10) 100
因为我们在 args
变量前添加了一个 *
前缀,函数的所有其它的额外参数都将传递到 args
中,并作为一个元组予以储存。如果采用的是 **
前缀,则额外的参数将被视为字典的键值—值配对。
装饰器
装饰器(Decorators)是应用包装函数的快捷方式。这有助于将某一功能与一些代码一遍又一遍地“包装”。举个例子,我为自己创建了一个 retry
装饰器,这样我可以将其运用到任何函数之中,如果在一次运行中抛出了任何错误,它就会尝试重新运行,直到最大次数 5 次,并且每次运行期间都会有一定的延迟。这对于你在对一台远程计算机进行网络调用的情况十分有用:
from time import sleepfrom functools import wrapsimport logginglogging.basicConfig()log = logging.getLogger("retry")def retry(f): @wraps(f) def wrapped_f(*args, **kwargs): MAX_ATTEMPTS = 5 for attempt in range(1, MAX_ATTEMPTS + 1): try: return f(*args, **kwargs) except: log.exception("Attempt %s/%s failed : %s", attempt, MAX_ATTEMPTS, (args, kwargs)) sleep(10 * attempt) log.critical("All %s attempts failed : %s", MAX_ATTEMPTS, (args, kwargs)) return wrapped_fcounter = 0@retrydef save_to_database(arg): print("Write to a database or make a network call or etc.") print("This will be automatically retried if exception is thrown.") global counter counter += 1 # 这将在第一次调用时抛出异常 # 在第二次运行时将正常工作(也就是重试) if counter < 2: raise ValueError(arg)if __name__ == '__main__': save_to_database("Some bad value")输出:$ python more_decorator.pyWrite to a database or make a network call or etc.This will be automatically retried if exception is thrown.ERROR:retry:Attempt 1/5 failed : (('Some bad value',), {})Traceback (most recent call last): File "more_decorator.py", line 14, in wrapped_f return f(*args, **kwargs) File "more_decorator.py", line 39, in save_to_database raise ValueError(arg)ValueError: Some bad valueWrite to a database or make a network call or etc.This will be automatically retried if exception is thrown.
这个装饰器语法有点奇怪,但还是蛮好用的。