标签列表

everest33

自制力

导航

python学习笔记

# -*- coding: UTF-8 -*-
from PIL import Image
im = Image.open("lfc.jpg")
width, height = im.size

file = open("file.txt", "w")

for i in range(height):
    for j in range(width):
        color = im.getpixel((j, i))
        colorSum = color[0]+color[1]+color[2]
        # print(colorSum, end=",")
        if (colorSum <400):
            file.write("0")
        else:
            file.write("1")
    file.write("\n")
file.close()

 


http://blog.csdn.net/birdzb/article/details/48582765
核心编程答案

1, python为什么不需要声明函数类型

 和不用声明变量一样,Python不用去声明函数的返回类型,是由于其“弱类型”的语言特性决定的。
 在其他语言中,例如C/C++语言中在存储一个数据之前,都需要在内存中给这个数据开辟一个固定的内存空间,并给这个类型空间指定一个唯一的 id(变量名),然后才把要存储的数据放到这个变量名对应的内存空间中。而Python的做法,是`以数据为中心`,上来就把要存储的数据放到内存,然后再去用一个变量名`引用`这个数据。

2,  核心模块: random
当你的程序需要随机数功能时,random 模块就能派上用场。该模块包含多个伪随机数发生
器,它们均以当前的时间戳为随机数种子。这样只要载入这个模块就能随时开始工作。下面列
出了该模块中最常用的函数:
randint(a, b)返回[a, b](包括a,b)之间的一个随机int数
randrange() 它 接 受 和 range() 函 数 一 样 的 参 数 , 随 机 返 回range([start,]stop[,step])结果的一项
uniform() 几乎和 randint()一样,不过它返回的是二者之间的一个浮点数(包括范围下限,不包括范围上限)。
random() 类似 uniform() 只不过下限恒等于 0.0,上限恒等于 1.0(包括范围下限,不包括范围上限)
choice() 随机返回给定序列的一个元素

shuffle()  #洗牌,随机播放shuffle play

random.shuffle的函数原型为:random.shuffle(x[, random]),用于将一个列表中的元素打乱。如:

  1. p = ["Python", "is", "powerful", "simple", "and so on..."]  
  2. random.shuffle(p)  
  3. print p  
  4. #---- 结果(不同机器上的结果可能不一样。)  
  5. #['powerful', 'simple', 'is', 'Python', 'and so on...']  

sample()

random.sample的函数原型为:random.sample(sequence, k),从指定序列中随机获取指定长度k的列表片断。sample函数不会修改原有序列。无论输入序列是什么类型,返回值永远是列表

 

3,  可哈希的 (hashable)   之含义

hash是一种函数映射,称为hash函数,y=hash_func(x),可hash就是指对于一个对象x有其对应的y。在python内部是通过字典key的hash值来对应内存中的value地址的,所以两个相同hash的key就表示同一个了,而不可hash的对象自然也不能作为字典的key。

不可变类型都是可哈希的,如字符串,数字,元组(元组元素必须也都是可哈希的)等等; 而可变类型一般都是不可哈希的,如列表,字典等,但也有一些可变对象(很少)是可哈希的,它们可以做字典的键,但很少见。举一个例子,一个实现了__hash__() 特殊方法的类。因为__hash__()方法返回一个整数,所以仍然是用不可变的值(做字典的键)。

为什么键必须是可哈希的?解释器调用哈希函数,根据字典中键的值来计算存储你的数据的位
置。如果键是可变对象,它的值可改变。如果键发生变化,哈希函数会映射到不同的地址来存储数
据。如果这样的情况发生,哈希函数就不可能可靠地存储或获取相关的数据。选择可哈希的键的原
因就是因为它们的值不能改变。

 4,记录一个题目、

编写计算器。 这个练习取材于 http://math.hws.edu/ 在线免费 Java 教材中的练习
12.2。编写一个程序允许用户选择两个集合:A 和 B, 及运算操作符。例如,in, not in, &, |, ^, <,
<=, >, >=, ==, !=, 等. (你自己定义集合的输入语法,它们并不一定要像 Java 示例中那样用方括
号括住。)解析输入的字符串,按照用户选择的运算进行操作。你写的程序代码应该比 Java 版本的
该程序更简洁。

def calc():
seta = input("input 1st set,separated by ','>>>")
setb = input("input 2nd set>>>")
operator = input('''
input operator:
('&', '|', '^', '-', 'in', 'not in',
'<','<=', '>', '>=', '==', '!='
>>>>>>''')
lista = seta.split(',')
listb = setb.split(',')
seta = set(lista)
setb = set(listb)
# print(seta)
# print(setb)
repra = repr(seta)
reprb = repr(setb)
repro = repr(operator)
ret = eval(repra + eval(repro) + reprb) #先把符号eval(),然后把表达式eval()
return ret
解释一下:repr(obj)会将obj变为obj的字符串表示形式,即外层加上双引号,对于字符串也是,如repr("good")显示为"'good'"。而eval(repr(obj))会将双引号去掉,重新变为原来的类型。eval(repr("good"))显示为
"good"。
对于上面的例子:
repra = "{1,2}", repro = "'&'", reprb = "{11,22}"
repra + repro +reprb===>> "{1,2}'&'{11,22}"
repra + eval(repro) + reprb====>> "{1,2}&{11,22}",再次对此结果执行eval()就可以运算了
其实不必把字符串形式的运算符进行repr()再eval(),直接用原始字符串形式和repr()后的集合的字符串形式连接,然后执行eval()即可.
5  关于dict.popitem()的解释
文档里说 Remove and return an arbitrary (key, value) pair from the dictionary。但在测试的时候发现我这个win python3.6版本总是返回最后一个键值对,并不是文档中所说的”任意的“。稍微翻阅了资料,发现网上有人总是返回第一个键值对。于是可理解如下:
这是标准和实现的关系。 arbitrary (key, value) pair from the dictionary说的是任意一个,这是标准。然后python这个版本的对标准的实现是弹出第一个键值对,也没有错啊,是不是。对不同版本来说是任意的,对某个固定的版本是固定的某个键值对。
6  关于逻辑运算 not and or
1>此三者运算的优先级为: not > and > or; 并且三个的优先级都比 比较运算符 优先级低
2>一个hack代码:smaller = (x<y and [x] or [y])[0];实际上就是三目运算表达式。
        smaller = x if x < y else y

7  while else或for else结构
  ①只有循环完成才会执行else语句。见下例
    for i in range(5): #[0,1,2,3,4]
      if i == 4: #程序没有机会判断是否执行完循环结构,所以else语句并不会执行。改为 i==5 else语句就会执行
        break
    else:
      print("I am executed")
  
  ①只要循环完成就会执行else语句。
    for i in range(5):
      if i == 3:
        continue #这里跳过了一个循环元素,但是程序最终能够执行完 整个循环,所以else语句会被执行。改为i==4同样也会执行else语句
    else:
      print("i am executed")

8  迭代器
  1  根本上说, 迭代器就是有一个 next() 方法的对象【python3里只有__next__()】, 而不是通过索引来计数. 当你或是一个循环机制(例如 for 语句)需要下一个项时,
    调用迭代器的 next() 方法就可以获得它. 条目全部取
出后, 会引发一个 StopIteration 异常, 这并不表示错误发生, 只是告诉外部调用者, 迭代完成.
  2  文件的next()方法实际就是readline()方法。
  

   3 记住,在迭代可变对象的时候修改它们并不是个好主意。对于列表仍可继续执行下去,只是结果会受到影响。而对于字典则直接会停止执行。

    关于列表如何受影响,例子如下:对于一个列表[1, 2, 3, 4],假设迭代第一次过后,删除list[0],则迭代器下一次会继续迭代第二个元素,而此时列表已是[2, 3, 4],第一个元素此时是3,所以

    2整个元素就被漏过去了。

   4 除列表外的其他序列都是不可变的, 所以危险就发生在这里. 一个序列的迭代器只是记录你当前到达第多少个元素, 所以如果你在迭代时改变了元素, 更新会立即反映到你所迭代的条目上.
    在迭代字典的 key 时, 你绝对不能改变这个字典. 使用字典的 keys() 方法是可以的, 因为keys() 返回一个独立于字典的列表. 而迭代器是与实际对象绑定在一起的, 它将不会继续执行下去:
    >>> myDict = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
    >>> for eachKey in myDict:
      ...print eachKey, myDict[eachKey]
      ..del myDict[eachKey]
      ... a 1
      Traceback (most recent call last):
      File "", line 1, in ?
      RuntimeError: dictionary changed size during iteration

  5 如何创建迭代器: 对一个对象调用内建函数 iter() 就可以得到它的迭代器。或者调用对象的__iter__()内建方法
    iter()函数官方文档:

iter(object[, sentinel])  # sentinel 哨兵,守卫
Return an iterator object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, object must be a collection object which supports the iteration protocol (the __iter__() method), or it must support the sequence protocol (the __getitem__() method with integer arguments starting at 0). If it does not support either of those protocols, TypeError is raised. If the second argument, sentinel, is given, then object must be a callable object. The iterator created in this case will call object with no arguments for each call to its __next__() method; if the value returned is equal to sentinel, StopIteration will be raised, otherwise the value will be returned.
See also Iterator Types.
One useful application of the second form of iter() is to read lines of a file until a certain line is reached. The following example reads a file until the readline() method returns an empty string:

with open('mydata.txt') as fp:
  for line in iter(fp.readline, ''):    #是readline而不是readline()
    process_line(line)

翻译一下:如果只有一个参数,那么此参数对象必须支持__iter__()方法或者__getitem__()方法。而如果有两个参数,那么对象必须是能被调用的对象[其实更像是函数,实际上老版本的iter()函数有两张形式iter(obj)    iter(func, sentinel )  ],迭代器每次都会调用object,如果object的返回值等于‘哨兵’的值,则迭代停止【object的这个返回值不会被返回给迭代器】,否则返回这个值给迭代器。

自己写的一个 粗糙的 例子:

def abc():
return "ha"
aa = iter(abc, 'ha') # 如果这里写'ha',则这个迭代器里没有任何值,如果不是'ha',则下面迭代的时候会无休止的输出 'ha'
for i in aa:
print(i)   
9  关于window下python读取文件的一个发现
  尽管在win下换行符是\r\n[CRLF],但是测试时发现[win7 python3.6],python在win下读取文件内容时,换行符是\n,测试方法是:
  f = open('log.txt', 'r', encoding='utf-8')
  print(f.readline().find('\r')) # 返回结果是-1,即没有找到。
  print(f.readline().find('\r\n')) # 返回结果是-1,即没有找到。
  
print(f.readline().find('\n')) # 返回\n的位置,找到了。
  另外,str.strip()方法默认去除空格,换行符\n也会被去除
  [更新]刚读到了这个发现的原因:叫做 通用换行符支持(UNS) universal newlines support

不同平台用来表示行结束的符号是不同的, 例如 \n, \r, 或者 \r\n . 所以, Python 的解释器也要处理这样的任务, 特别是在导入模块时分外重要。 你难道不希望 Python 用相同的方式处理所有文件吗?

这就是 UNS 的关键所在, 作为 PEP 278 的结果, Python 2.3 引入了 UNS. 当你使用 'U' 标志打开文件的时候, 所有的行分割符(或行结束符, 无论它原来是什么)通过 Python 的输入方法(例如 read*() )返回时都会被替换为换行符 NEWLINE(\n). ('rU' 模式也支持 'rb' 选项) . 这个特性还支持包含不同类型行结束符的文件. 文件对象的 newlines 属性会记录它曾“看到的”文件的行结束符.

如果文件刚被打开, 程序还没有遇到行结束符, 那么文件的 newlines 为 None .在第一行被读取后, 它被设置为第一行的结束符f = open('log', 'r')  f.readline()  print(list(f.newlines))  输出结果是 ['\r', '\n']】. 如果遇到其它类型的行结束符, 文件的 newlines 会成为一个包含每种格式的元组. 注意 UNS 只用于读取文本文件. 没有对应的处理文件输出的方法.

在编译 Python 的时候,UNS 默认是打开的. 如果你不需要这个特性, 在运行 configure 脚本时,你可以使用 --without-universal-newlines 开关关闭它. 如果你非要自己处理行结束符, 请查阅核心笔记,使用 os 模块的相关属性.

 

 [补充]对于python程序来讲,read*()是输入(从文件到python),write*()是输出(从python到文件)

 

10,  文件读取方法  truncate()的疑惑  

文件对象还有一个 truncate() 方法, 它接受一个可选的 size 作为参数. 如果给定, 那么文件将被截取到最多 size 字节处. 如果没有传递 size 参数, 那么默认将截取到文件的当前位置.例如, 你刚打开了一个文件, 然后立即调用 truncate() 方法, 那么你的文件(内容)实际上被删除,这时候你是其实是从 0 字节开始截取的( tell() 将会返回这个数值 ).

自己在测试时发现,如果打开文件后读取了一些字节后(或者一行),此时调用truncate()方法,如果不传参数,并没有像想象的那样截取到当前位置,而是整个文件并没有任何变化(或者说截取到了文件末尾)。找资料也没有找到相关的内容。。测试代码如下:

f = open('log', 'r+')  print(f.read(3))  print(f.tell())  print(f.truncate())  print(f.readline())

11,  标准文件

一般说来, 只要你的程序一执行, 那么你就可以访问三个标准文件. 它们分别是标准输入(一般是键盘), 标准输出(到显示器的缓冲输出)和标准错误(到屏幕的非缓冲输出). (这里所说的"缓冲"和"非缓冲"是指 open() 函数的第三个参数.) 这些文件沿用的是 C 语言中的命名, 分别为stdin , stdout 和 stderr . 我们说"只要你的程序一执行就可以访问这三个标准文件", 意思是这些文件已经被预先打开了, 只要知道它们的文件句柄就可以随时访问这些文件.

Python 中可以通过 sys 模块来访问这些文件的句柄. 导入 sys 模块以后, 就可以使用sys.stdin , sys.stdout 和 sys.stderr 访问. print 语句通常是输出到 sys.stdout ; 而内建raw_input() 则通常从 sys.stdin 接受输入.

记得 sys.* 是文件, 所以你必须自己处理好换行符. 而 print 语句会自动在要输出的字符串后加上换行符。

【自注】python从sys.stdin文件读取用户输入(对python来说读取文件内容叫输入[从文件输入到python]),而把内容输出到stdout文件中(对python来说写入文件叫输出[从python输出到文件])

 12,  闭包 

追踪闭包变量:【闭包变量即是不在全局作用域也不在自己的局部作用域中的变量】
output = '<int %r id=%#0x val=%d>'
w = x = y = z = 1


def f1():
x = y = z = 2

def f2():
y = z = 3

def f3():
z = 4
print(output % ('w', id(w), w))
print(output % ('x', id(x), x))
print(output % ('y', id(y), y))
print(output % ('z', id(z), z))

clo = f3.__closure__
if clo:
print("f3 closure vars:", [str(c) for c in clo])
else:
print("no f3 closure vars")
f3()

clo = f2.__closure__
if clo:
print("f2 closure vars:", [str(c) for c in clo])
else:
print("no f2 closure vars")
f2()


clo = f1.__closure__
if clo:
print("f1 closure vars:", [str(c) for c in clo])
else:
print("no f1 closure vars")
f1()
========================================================
输出如下:

no f1 closure vars   # f1没有闭包变量; x是来自f1的闭包变量
f2 closure vars: ['<cell at 0x7f5fbfc81a98: int object at 0x8eb880>']  #f2的闭包变量是x, 来自f2的闭包变量是y
f3 closure vars: ['<cell at 0x7f5fbfcc2b08: int object at 0x8eb8a0>', '<cell at 0x7f5fbfc81a98: int object at 0x8eb880>'] # f3的闭包变量是x和y,没有来自f3的闭包变量
<int 'w' id=0x8eb860 val=1>
<int 'x' id=0x8eb880 val=2>
<int 'y' id=0x8eb8a0 val=3>
<int 'z' id=0x8eb8c0 val=4>

 

13,  lambda表达式的作用域

python2.1之前和之后有所不同,这里只讲2.1之后的情况

 

python 的 lambda 匿名函数遵循和标准函数一样的作用域规则。一个 lambda 表达式定义了新的作用域,就像函数定义,所以这个作用域除了局部 lambda/函数,对于程序其他部分,该作用域都是不能对进行访问的。但是有一种情况比较特殊,就是如果lambda表达式是在一个函数内部,此时lambda的作用域和这个函数的作用域是相同的。例子如下:

x = 10

def foo():

  y = 5

  bar = lambda : x + y     #python2.1之后这里的y和上面一行的y实际上是同一个但是在python2.1之前,这个y是只属于lambda表达式的局部变量

  print(bar())

 

 14,  执行环境

(一)、代码对象:是指 python 语句,赋值,表达式,甚至还有模块。如果要执行 python 代码,那么该代码必须先要转换成字节编译的代码(又称字节码)。这才是真正的代码对象。

几个内建函数:

compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1) 

compile()函数允许程序员在运行时刻迅速生成代码对象,然后就可以用 exec 语句或者内建函数 eval()来执行这些对象或者对它们进行求值。一个很重要的观点是:exec 和 eval()都可以执行字符串格式的 Python 代码。当执行字符串形式的代码时,每次都必须对这些代码进行字节编译处理。compile()函数提供了一次性字节代码预编译,以后每次调用的时候,都不用编译了。compile 的三个参数都是必需的,第一参数代表了要编译的 python 代码。第二个字符串,虽然是必需的,但通常被置为空串。该参数代表了存放代码对象【自注:更准确的是代码对象是从这个文件里读出来的】的文件的名字(字符串类型)。compile 的通常用法是动态生成字符串形式的 Python 代码, 然后生成一个代码对象——代码显然没有存放在任何文件。【自注,即使是从一个文件里读出来的,这个参数也可以设置为空:''】最后的参数是个字符串,它用来表明代码对象的类型。有三个可能值:
'eval'      可求值的表达式[和 eval()一起使用]
'single'   单一可执行语句[和 exec 一起使用]
'exec'    可执行语句组[和 exec 一起使用]

eval()表达式求值,后者可以为字符串或内建函数 complie()创建的预编译代码对象。

和 eval()相似,exec(obj)执行代码对象或字符串形式的 python 代码.被执行的对象(obj)可以只是原始的字符串,比如单一语句或是语句组,它们也可以预编译成一个代码对象(分别用'single'和'exec"参数)

【自己理解】代码对象是在指源代码编译成的字节码。compile()函数生成的便是此字节码。eval()和exec()函数可以直接执行字节码生成结果;如果这两个函数接受的是字符串代码,他们会首先把字符串代码编译成字节码,然后再执行字节码生成结果。  另外,eval()是对表达式求值,返回的就是表达式的值,无法执行语句组。而exec()执行语句组(可以只有一句),返回值是None.它不会对表达式求值。比如exec("33+3")就相当于你在代码里直接写 33+3。或者exec('a=33+3')相当于在代码里直接写a=33+3 。 

 (二)、执行其他非python程序

【新增:另一篇参考文章,关于os模块】http://www.cnblogs.com/now-fighting/p/3534847.html

fork()和exec*()详解:(都是在os模块中)

首先一点,fork()只在unix系统中有效,windows中是没有用的。

【参考文章】http://blog.csdn.net/csujiangyu/article/details/44917483

 Python中,任务并发一种方式是通过进程分支来实现的.在Linux系统在,通过fork()方法来实现进程分支.

  • fork()调用后会创建一个新的子进程,这个子进程是原父进程的副本.子进程可以独立父进程外运行.
  • fork()是一个很特殊的方法,一次调用,两次返回.
  • fork()它会返回2个值,一个值为0,表示在子进程返回;另外一个值为非0,表示在父进程中返回子进程ID.

以下只能在linux中运行,不能在window下运行.

进程分支fork()

实例如下:

#!/usr/bin/python
#coding=utf-8
import os

def child():
    print('hello from child', os.getpid())【自注:这里是获得子程序的pid】
    os._exit(0)
def parent():
    pid = os.fork()【自注:os.fork()有两个返回值,一个是子程序的返回值,永远为0;另一个是父程序的返回值,返回子程序的pid】
    if pid == 0:【自注:这里是fork()中子程序的返回值】
        child()
        print('fork child process error!')#如果打印该字符串,说明调用child()出错
    else:
        print('hello from parent', os.getpid(), pid)【自注:这里的os.getpid()是获取父程序的pid(注意这是父程序的pid),而pid是fork()父程序的返回值,即子程序的pid】

parent()

运行结果如下:

('hello from parent', 29888, 29889)
('hello from child', 29889)

从结果不难看出, child()后的print字符并没有打印处理,说明调用child()是没有返回的【自注:没有打印是因为os._exit(0)的作用,如果把这行代码注释掉就会打印出后面的字符】.

fork和exec的组合

从上面的例子来看,调用child()方法后就直接退出了.但在实际的应用中,我们希望分支出来的子进程能独立运行另外一个新的程序.这时需要用到exec方法替换子进程,并且替换后进程的pid不会改变.exec方法不会返回.

【以下8个方法的自己理解:】

首先解释下参数,ececl(path, arg0, arg1,.....):第一个参数是程序名称,需要是程序的完整路径名(包括程序名本身,win下不带.exe)[如果是带p的函数,则不需要路径名,直接写程序名本身]。重点要说的是第二个参数,即arg0。参考一篇文章  http://blog.chinaunix.net/uid-23960482-id-119799.html 。这个参数是可以随便传的(但要是字符串型),但不要为空''或None。除去这个参数,其他剩余的所有参数是命令的直接映射。

  举个例子: 在终端执行python xxx.py,对应到这个函数中应该是execl(r'D:\software\python36\python', 'a_arbitrary_string', 'xxx.py的路径[相对的或绝对的路径])。另外如果xxx.py的路径错了,当前程序不会出错,但是新程序会报错(这是新程序的命令了);

  再举个例子,终端执行命令:python -m a_python_module。对应到这个函数中应该是execl(r'D:\software\python36\python', 'sadf', '-m', 'a_python_module')。同样如果a_python_module找不到,当前程序不会出错,新程序会报错。

"v" 和 "l"的区别在于传入参数的方式不同,v是把所有参数放在列表或元组里传,l是一个一个的传入多个参数。[具体来说是从第二个参数开始的,第一个参数都是程序名,都是一样的]

"p"的有无:p是影响第一个参数,有p则表示新程序的搜索路径是当前执行脚本的搜索路径[搜索路径就是环境变量,这句化的意思就是用环境变量定位新程序文件],此时第一个参数可以直接写程序名,如python,无p则第一个参数必须是程序的完整路径名,可以是绝对路径也可以是相对路径(包括程序名本身,win下不带.exe),如r'D:\software\python36\python'或者如 r'../../software/python36/python'。

"e"的有无:这个查了很多资料,不是特别理解。自己理解的是(应该是对的):这个参数是定义新程序需要的环境变量,传如一个mapping对象后,新程序的环境变量就变为了这个mapping对象中定义的值。 注意,这个参数影响的是新程序的环境变量,对当前程序并没有任何的影响。也就是说,有e的函数,如果没有p,第一个参数也是要有程序的完整路径名。

 最后,其实这些函数是linux系统编程中的函数,参考如下一篇很好的文章:linux系统编程之进程(五):exec系列函数

首先解释一下exec相关的8个方法组:

  • os.execv(program, cmdargs) 
    基本的”v”执行形式,需要传入可执行的程序名,以及用来运行程序的命令行参数字符的列表或元组.

  • os.execl(program, cmdarg1, cmdarg2, …, cmdargN) 
    基本的”l”执行形式,需要传入可执行的程序名,以及用来运行程序的命令行多个字符参数.

  • os.execvp(program, args) 
    “p”模式下,基本的”v”执行形式,需要传入可执行的程序名,以及用来运行程序的命令行参数字符的列表或元组.运行新程序的搜索路径为当前文件的搜索路径.

  • os.execlp(program, cmdarg1, cmdarg2, …, cmdargN) 
    “p”模式下,基本的”l”执行形式,需要传入可执行的程序名,以及用来运行程序的命令行多个字符参数.运行新程序的搜索路径为当前文件的搜索路径.

  • os.execve(program, args, env) 
    “e”模式下,基本的”v”执行形式,需要传入可执行的程序名,以及用来运行程序的命令行参数字符的列表或元组.最后还要传入运行新程序的需要的环境变量env字典参数.

  • os.execle(program, cmdarg1, cmdarg2, …, cmdargN, env) 
    “e”模式下,基本的”l”执行形式,需要传入可执行的程序名,以及用来运行程序的命令行多个字符参数.最后还要传入运行新程序的需要的环境变量env字典参数.

  • os.execvpe(program, args, env) 
    在”p”和”e”的组合模式下,基本的”v”执行形式,需要传入可执行的程序名,以及用来运行程序的命令行参数字符的列表或元组.最后还要传入运行新程序的需要的环境变量env字典参数.运行新程序的搜索路径为当前文件的搜索路径.

  • os.execlpe(program, cmdarg1, cmdarg2, …, cmdargN, env) 
    在”p”和”e”的组合模式下,基本的”l”执行形式,需要传入可执行的程序名,以及用来运行程序的命令行多个字符参数.最后还要传入运行新程序的需要的环境变量env字典参数.运行新程序的搜索路径为当前文件的搜索路径.

newprocess.py代码如下:

#!/usr/bin/python
#coding=utf-8
import os

def child():
    print('hello from child', os.getpid())
    os._exit(0)

child()

主代码如下:

#!/usr/bin/python
#coding=utf-8
import os

def child():
    print('hello from child', os.getpid())
    os._exit(0)

def parent():
    pid = os.fork()
    if pid == 0:
        os.execlp('python', 'python', 'newprocess.py')
        assert False, 'fork child process error!' 【#自注,如果上面一句执行成功,这句话是不会打印的,因为exec*()把子进程替换掉了,而这句话属于子进程,没有机会被运行了】
    else:
        print('hello from parent', os.getpid(), pid)
parent()

输出如下:

$ python TestFork.py 
('hello from parent', 30791, 30792)
$ ('hello from child', 30792)
15,  面向对象OOP
新式类和经典类(老式类)的区别:从2.2版本开始有了新式类。现在基本都用新式类,这里只是简单说明一下新老的区别,也有助于理解新式类的一些东西。

老式类的类(的类型)是类对象,实例的类型是 是实例对象,在这两个对象类型之间没有任何关系,除了实例的__class__属性引用了被实例化以得到该实例的类。
你创建一个老式类时,你并没有创建一个新的类型,而仅仅是一个类对象。

新式类的类(的类型)是类型对象(type),实例的类型是用以创建这个实例的类,实例本身是一个类对象【想象下,实例就是类对象调用[用小括号表示调用]的返回值】。更进一步的说明,现在你每创建一个新式类,你就创建了一个新的类型(关于类型,int
,float,str等等都是一个类型),而实例的类型就是它所调用的那个类。

新老类的区别简单用图标示一下:
新式类图例:
class type  
|----int
|----float
|----MyClass1
   |----myInstance1
   |----myInstance2
|----MyClass2
   |----myInstance1
   |----myInstance2

老式类图例:
class type
|----int
|----float
|----class
   |----MyClass1
   |----MyClass2
|----instance
   |----myInstance1
   |----myInstance2

15,  面向对象
①,将对象(实例)声明为迭代器的方法
  只要在类中定义两个方法即可把一个对象声明为迭代器,而且这两个方法缺一不可。分别是__iter()__和__next__()。【在python2中是next()方法,但在python3中只有__next__()方法】。其中的__iter__()方法很简单,只要返回self就可以了,即:
  def __iter__(self):
    return self

②,
特殊方法__getattribute__()。
Python 类有一个名为__getattr__()的特殊方法,它仅当属性不能在实例的__dict__或它的类(类的__dict__),或者祖先类(其__dict__)中找到时,才被调用。
Python还有一个名为__getattribute__()的特殊方法,它使用起来,类似__getattr__(),不同之处在于,当属性被访问时,它就一直都可以被调用,而不局限于不能找到的情况。
说明:关于__getattribute__()方法,这个方法优先级非常高,如果定义了此方法,则访问属性时,无论这个属性是否存在都会首先会调用这个方法。
   当__getattribute__()和__getattr__()都存在时,会先调用前者,一般不会再调用后者,但有两种例外情况。1是在__getattribute__()中主动调用了后者,而是前者引发了
AttributeError异常(只能是此异常)

注意一个例子

③描述符总结

你已经看到描述符是怎么工作的。静态方法、类方法、属性(见下面一节),甚至所有的函数都是描述符。想一想:函数是 Python 中常见的对象。有内置的函数、用户自定义的函数、类中定义的方法、静态方法、类方法。这些都是函数的例子。 它们之间唯一的区别在于调用方式的不同。通常,函数是非绑定的。虽然静态方法是在类中被定义的,它也是非绑定的。但方法必须绑定到一个实例上,类方法必须绑定到一个类上,对不?一个函数对象的描述符可以处理这些问题,描述符会根据函数的类型确定如何“封装”这个函数和函数被绑定的对象,然后返回调用对象。它的工作方式是这样的:函数本身就是一个描述符,函数的__get__()方法用来处理调用对象,并将调用对象返回给你。描述符具有非常棒的适用性,因此从来不会对 Python 自己的工作方式产生影响。

 

 



 











posted on 2017-03-12 18:07  everest33  阅读(370)  评论(0编辑  收藏  举报