构造函数(constructor),类定义中的初始化方法,命名为_init_。然而,构造函数不同于普通方法的地方在于,将在对象创建后自动调用它。
Python提供了魔法方法del,也称作析构函数(destructor)。这个方法在对象被销毁(作为垃圾被收集)前被调用,但鉴于你无法知道准确的调用时间,建议尽可能不要使用del。
重写的构造函数必须调用其超类的构造函数,以确保基本的初始化得以执行。为此,有两种方法:调用未关联的超类构造函数,以及使用函数super。super().init()
def __init__(self):
super().__init__()
在Python中,协议通常指的是规范行为的规则,有点类似于第7章提及的接口。协议指定应实现哪些方法以及这些方法应做什么。在Python中,多态仅仅基于对象的行为(而不基于祖先,如属于哪个类或其超类等),因此这个概念很重要:其他的语言可能要求对象属于特定的类或实现了特定的接口,而Python通常只要求对象遵循特定的协议。因此,要成为序列,只需遵循序列协议即可。
__len__(self)
__getitem__(self, key)
__setitem__(self, key,value)
__delitem__(self, key)
通过存取方法定义的属性通常称为特性(property)。 get(),set()
函数 property,通过调用函数property并将存取方法作为参数(获取方法在前,
设置方法在后)创建了一个特性,然后将名称size关联到这个特性。这样,你就能以同样的方式对待width、height和size,而无需关心它们是如何实现的.
class Rectangle:
def __init__ (self):
self.width = 0
self.height = 0
def set_size(self, size):
self.width, se
多态、封装、方法、属性、超类和继承
每当无需知道对象是什么样的就能对其执行操作时,都是多态在起作用
>>> 'abc'.count('a')
1
>>> [1, 2, 'a'].count('a')
1
如果有一个变量x,你无需知道它是字符串还是列表就能调用方法count:只要你向这个方法提供一个字符作为参数,它就能正常运行。
多态形式是Python编程方式的核心,有时称为鸭子类型。这个术语源自如下说法:“如果走起来像鸭子,叫起来像鸭子,那么它就是鸭子。”有关鸭子类型的详细信息,请参阅http://en.wikipedia.org/wiki/Duck_typing。
封装(encapsulation)指的是向外部隐藏不必要的细节。这听起来有点像多态(无需知道对象的内部细节就可使用它)。这两个概念很像,因为它们都是抽象的原则。它们都像函数一样,可帮助你处理程序的组成部分,让你无需关心不必要的细节。但封装不同于多态。多态让你无需知道对象所属的类(对象的类型)就能调用其方法,而封装让你无需知道对象的构造就能使用它。
如果
o将其名称存储在全局变量global_name中,如果尝试创建多个OpenObject对象,将出现问题,因为它们共用同一个变量。避免干扰全局变量,就需要将名称“封装”在对象中。使用属性而非全局变量。
在Python中,表示类约定使用单数并将首字母大写,如Bird和Lark。
在较旧的Python版本中,类型和类之间泾渭分明:内置对象是基于类型的,而自定义对象是基于类的。因此,你可以创建类,但不能创建类型。在较新的Python 2版本中,这种差别不那么明显。在Python 3中,已不再区分类和类型了。
如果能直接访问ClosedObject(对象c所属的类)的属性name,就不需要创建方法setName和getName了。关键是其他程序员可能不知道(也不应知道)对象内部发生的情况。例如,ClosedObject可能在对象修改其名称时向管理员发送电子邮件。这种功能可能包含在方法set_name中。但如果直接设置c.name,结果将如何呢?什么都不会发生——根本不会发送电
Python 断言 并行迭代 异常
如果知道必须满足特定条件,程序才能正确地运行,可在程序中添加assert语句充当检查点,这很有帮助。还可在条件后面添加一个字符串,对断言做出说明。
>>> age = -1
>>> assert 0 < age < 100, 'The age must be realistic'
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AssertionError: The age must be realistic
有时候,你可能想同时迭代两个序列。假设有下面两个列表:
names = ['anne', 'beth', 'george', 'damon']
ages = [12, 45, 32, 102]
一个很有用的并行迭代工具是内置函数zip,它将两个序列“缝合”起来,并返回一个由元组组成的序列。返回值是一个适合迭代的对象,要查看其内容,可使用list将其转换为列表。
>>> list(zip(names, ages))
[('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]
“缝合”后,可在循环中将元组解包。
for name, age in zip(names, ages):
print(name, 'is', age, 'years old')
函数zip可用于“缝合”任意数量的序列。需要指出的是,当序列的长度不同时,函数zip将在最短的序列用完后停止“缝合”。
>>> list(zip(range(5), range(100000000)))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
使用内置函数enumerate。
for index, string in enumerate(strings):
xxxxxx
可以使用循环来确保用户输
函数open的参数mode的最常见取值
值 | 描 述 |
---|---|
'r' | 读取模式(默认值) |
'w' | 写入模式 |
'x' | 独占写入模式 |
'a' | 附加模式 |
'b' | 二进制模式(与其他模式结合使用) |
't' | 文本模式(默认值,与其他模式结合使用) |
'+' | 读写模式(与其他模式结合使用) |
显式地指定读取模式的效果与根本不指定模式相同。写入模式让你能够写入文件,并在文件不存在时创建它。
独占写入模式更进一步,在文件已存在时引发FileExistsError异常。在写入模式下打开文件时,既有内容将被删除(截断),并从文件开头处开始写入;
如果要在既有文件末尾继续写入,可使用附加模式。
'+'可与其他任何模式结合起来使用,表示既可读取也可写入。例如,要打开一个文本文件进行读写,可使用'r+'。(你可能还想结合使用seek,详情请参阅本章后面的旁注“随机存取”。)请注意,'r+'和'w+'之间有个重要差别:后者截断文件,而前者不会这样做。默认模式为'rt'.
每当调用f.write(string)时,你提供的字符串都将写入到文件中既有内容的后面。
>>> f = open('somefile.txt', 'w')
>>> f.write('Hello, ')
7
>>> f.write('World!')
6
>>> f.close()
读取也一样简单,只需告诉流你要读取多少个字符(在二进制模式下是多少字节),如下例所示:
>>> f = open('somefile.txt', 'r')
>>> f.read(4)
'Hell'
>>> f.read()
'o, World!
f.read() 读取了文件中余下的全部内容
模块sys的一节中,提到了三个标准流。
sys.stdin
一个标准数据输入源是sys.stdin。当程序从标准输入读取时,你可通过输入来提供文本,也可使用管道将标准输入关联到其他程序的标准输出.
sys.stdout
提供给print的文本出现在sys.stdout中,向input提供的提示信息
模块是为了用来下定义和重用的。
导入放在c:/python 下的hello.py 作为模块使用。模块导入只导入一次,第二次什么也不会发生,打破互相导入的死循环。如果一定要重新加载模块,使用importlib.reload()
>>> import sys
>>> sys.path.append('C:/python')
>>> import hello
Hello, world!
查看系统查找的全部路径有什么,除了sys.path , 标准做法是将模块所在的目录包含在环境变量PYTHONPATH中。
>>> import sys, pprint
>>> pprint.pprint(sys.path)
['C:\\Python35\\Lib\\idlelib',
'C:\\Python35',
'C:\\Python35\\DLLs',
'C:\\Python35\\lib',
'C:\\Python35\\lib\\plat-win',
'C:\\Python35\\lib\\lib-tk',
'C:\\Python35\\lib\\site-packages']
包含测试代码的模块,应该将代码放入if 条件中
if __name__ == '__main__': test()
模块存储在扩展名为.py的文件中,而包则是一个目录。要被Python视为包,目录必须包含文件init.py。如果像普通模块一样导入包,文件init.py的内容就将是包的内容。
要查明模块包含哪些东西,可使用函数dir,它列出对象的所有属性(对于模块,它列出所有的函数、类、变量等)。
[i for i in dir(copy) if not i.startswith("_")]
['Error', 'copy', 'deepcopy', 'dispatch_table', 'error']
>>> copy.__all__
['Error', 'copy', 'deepcopy']
all 的设置是在模块copy 是定义的
__all__ =
除了# 注释,有另一种编写注释的方式,就是添加独立的字符串。在有些地方,如def语句后面(以及模块和类的开头,),添加这样的字符串很有用。放在函数开头的字符串称为文档字符串(docstring),将作为函数的一部分存储起来。下面的代码演示了如何给函数添加文档字符串:
def square(x):
'Calculates the square of the number x.'
return x * x
可以像下面这样访问文档字符串:
>>> square.__doc__
'Calculates the square of the number x.'
单星号 * (元组)
前面有星号的参数将被放在元组中。
因此星号意味着收集余下的位置参数。如果没有可供收集的参数,params将是一个空元组。
>>> print_params_2('Nothing:')
Nothing:
()
与赋值时一样,带星号的参数也可放在其他位置(而不是最后),但不同的是,在这种情况下你需要做些额外的工作:使用名称来指定后续参数。单星号不会收集关键字参数。
>>> def in_the_middle(x, *y, z):
... print(x, y, z)
...
>>> in_the_middle(1, 2, 3, 4, 5, z=7)
1 (2, 3, 4, 5) 7
双星号 ** (字典)
要收集关键字参数,可使用两个星号。
>>> def print_params_3(**params):
... print(params)
...
>>> print_params_3(x=1, y=2, z=3)
{'z': 3, 'x': 1, 'y': 2}
如你所见,这样得到的是一个字典而不是元组。可结合使用这些技术。
def print_params_4(x, y, z=3, *pospar, **keypar):
print(x, y, z)
print(pospar)
print(keypar)
其效果与预期的相同。
>>> print_params_4(1
字符串(以及数和元组)是不可变的(immutable),这意味着你不能修改它们(即只能替换为新值)。因此这些类型作为参数没什么可说的,不会影响 外部原来的值。但如果参数为可变的数据结构(如列表)呢?
>>> def change(n):
... n[0] = 'Mr. Gumby'
>>> names = ['Mrs. Entity', 'Mrs. Thing']
>>> change(names)
>>> names
['Mr. Gumby', 'Mrs. Thing']
在这个示例中,修改了变量关联到的列表。下面再这样做一次,但这次不使用函数调用。
>>> names = ['Mrs. Entity', 'Mrs. Thing']
>>> n = names # 再次假装传递名字作为参数
>>> n[0] = 'Mr. Gumby' # 修改列表
>>> names
['Mr. Gumby', 'Mrs. Thing']
将同一个列表赋给两个变量时,这两个变量将同时指向这个列表。就这么简单。要避免这样的结果,必须创建列表的副本。对序列执行切片操作时,返回的切片都是副本。因此,如果你创建覆盖整个列表的切片,得到的将是列表的副本。
>>> names = ['Mrs. Entity', 'Mrs. Thing']
>>> n = names[:]
现在n和names包含两个相等但不同的列表。
>>> n is names
False
>>> n == names
True
现在如果(像在函数change中那样)修改n,将不会影响names。
>>> n[0] = 'Mr. Gumby'
>>> n
['Mr. Gumby', 'Mrs. Thing']
>>> names
['Mrs. Entity', 'Mrs. Thing']
下面来尝试结合使用这种技巧和函数change。
>>> change(names[:])
>>> names
['Mrs. Entity', 'Mrs. Thing']
注意到参数n包含的是副本,因此原始列表是安全的。
方法append用于将一个对象附加到列表末尾。
>>> lst = [1, 2, 3]
>>> lst.append(4)
>>> lst
[1, 2, 3, 4]
append也就地修改列表。这意味着它不会返回修
改后的新列表,而是直接修改旧列表。
# l 指向letterGirls['a'] , 然后 l 后面附加一串‘cc’ , letterGirls['a']的值也被修改,两者指向同一个列表,即原件相同,关联同一个列表
>>> l = letterGirls.setdefault('a', [])
>>> l
['alice', 'alice', 'aly', 'aa', 'bb']
>>> l.append('cc')
>>> l
['alice', 'alice', 'aly', 'aa', 'bb', 'cc']
>>> letterGirls
{'a': ['alice', 'alice', 'aly', 'aa', 'bb', 'cc'], 'b': ['bernice', 'bernice'], 'c': ['clarice', 'clarice']}
# 同理,引用同一个列表,其删作一个元素,另一个也修改
>>> b = l
>>> b
['alice', 'alice', 'aly', 'aa', 'bb', 'cc']
>>> del(b[3])
>>> l
['alice', 'alice', 'aly', 'bb', 'cc']
>>> b
['alice', 'alice', 'aly', 'bb', 'cc']
方法setdefault有点像get,因为它也获取与指定键相关联的值,但除此之外,setdefault
还在字典不包含指定的键时,在字典中添加指定的键-值对。
>>> d = {}
>>> d.setdefault('name', 'N/A')
'N/A'
>>> d
{'name': 'N/A'}
>>> d['name'] = 'Gumby'
>>> d.setdefault('name', 'N/A')
'Gumby'
>>> d
{'name': 'Gumby'}
# 如你所
推导并不是语句,而是表达式。它们看起来很像循环,通过列表推导,可从既有列表创建出新列表,这是通过对列表元素调用函数、剔除不想要的函数等实现的。推导功能强大,但在很多情况下,使用普通循环和条件语句也可完成任务,且代码的可读性可能更高。使用类似于列表推导的表达式可创建出字典。
括号 []
>>> [x * x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> [(x, y) for x in range(3) for y in range(3)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
>>> girls = ['alice', 'bernice', 'clarice']
>>> boys = ['chris', 'arnold', 'bob']
>>> [b+'+'+g for b in boys for g in girls if b[0] == g[0]]
['chris+clarice', 'arnold+alice', 'bob+bernice']
花括号 { }
>>> squares = {i:"{} squared is {}".format(i, i**2) for i in range(10)}
>>> squares[8]
'8 squared is 64'
在列表推导中,for前面只有一个表达式,而在字典推导中,for前面有两个用冒号分隔的表
达式。这两个表达式分别为键及其对应的值。
圆括号 ( )
生成器推导(也叫生成器表达式)。其工作原理与列表推导相同,但不是创建一个列表(即不立即执行循环),而是返回一个生成器,让你能够逐步执行计算。
sum(i ** 2 for i in range(10))
函 数 描 述
random() 返回一个0~1(含)的随机实数
getrandbits(n) 以长整数方式返回n个随机的二进制位
uniform(a, b) 返回一个a~b(含)的随机实数
randrange([start], stop, [step]) 从range(start, stop, step)中随机地选择一个数
choice(seq) 从序列seq中随机地选择一个元素
shuffle(seq[, random]) 就地打乱序列seq
sample(seq, n) 从序列seq中随机地选择n个值不同的元素
from random import sample
print('MY RED : {}'.format(sample(list(range(1,34,1)), 6)))
print('MY BLUE : {}'.format(sample(list(range(1,17,1)), 1)))
(venv) C:\Users\LTE-LCFeng\PycharmProjects\ssq\venv>python ssq.py
MY RED : [9, 1, 24, 26, 3, 6]
MY BLUE : [6]
范围 0~32767
echo $RANDOM
取小范围内随机数,对随机结果取模N , 可取到0~(N-)1 内的随机数
取1~ 6 的随机数,只需加1
echo $[$RANDOM%6+1]
在1-33 中取6个不重复的随机数,在1-16中取1随机数
#!/bin/bash
RED_D=33
BLUE_D=16
RED_MAX=6
count=0
touch RED.txt
while [ $count -lt ${RED_MAX} ]
do
flag=$(expr 6 - $count)
while [ $flag -gt 0 ]
do
flag=$(expr $flag - 1)
RED=$[$RANDOM%