1.raw_input() was renamed to input() python3后就已经将raw_input()与input合并了,统一为input
2.关于复杂的编码问题
2.1 原始的原始,ACSII码,用来记录英文与字符,因为语音开始就只会用到这些
2.2 由于中文需要有了GB2312,记录中文
2.3 由于各国有各国的编码,于是诞生了Unicode编码,一般 两个字节来表示一个字符 这样在表示英文的时候就会占用大量空间
2.4 由于Unicode占用空间,所以utf-8出现了,英文只占一个字节,中文是三个字节,这就是他们的相爱相杀
总结内存里面使用Unicode,存储 / 网络传输使用utf-8
3.tuple 只包含一个数据的时候,t = (1,),不要省略最后的','哦
>>> t = (1,)
>>>
>>> t
(1,)
>>> t = (1)
>>> t
1
4.dict 有一个很有意思的地方就是 dict有pop方法 d.pop(key) 对应的value也会删除
>>> d = {'a':1, 'b':2, 'c':'rookia'}
>>> d['a']
1
>>> d.pop('a')
1
>>> d
{'b': 2, 'c': 'rookia'}
5.set可以像数学一样求并集,交集
>>> s1 = set([1,2,3])
>>> s2 = set([2,3,4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}
6.关于可变与不可变思考
关于不可变字符串对其改变 a.replace('a','A') 这里的改变会通过返回值来返回改变结果,但是不可变a本身是不变的
而数组就不一样a.sort()就会直接改变a
>>> a = ['a','3','b','5']
>>> s = "abc"
>>> a.sort()
#不难发现a已经直接改变了
>>> a
['3', '5', 'a', 'b']
>>> s.replace('a','A')
'Abc'
#虽然返回结果是'Abc',但是s的值还是'abc'
>>> s
'abc'
7.函数
函数名 其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”
8.可变参数
如果已经有一个list或者tuple,要调用一个可变参数怎么办?Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去。
>>> def calc(*nums):
... for x in nums:
... print (x)
...
>>> calc(1,2,3,5)
1
2
3
5
>>> a = [13,4,5,6,7]
#如果已经有一个list或者tuple,要调用一个可变参数怎么办?
#Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去。
>>> calc(a[1], a[2], a[3], a[4])
4
5
6
7
>>> calc(*a)
13
4
5
6
7
>>>
9.关于指定命名关键字参数
指定命名关键字参数与关键字参数区别,指定命名关键字是对关键字的一个限制,允许函数中只能出现指定好的关键字,其他的都为错误的。
思考1:指定命名关键字参数,是否是关键字参数?
回答:答案是肯定的。
所以另外一个问题就来了——> 在调用函数时需要时用 params_func('a','b',c = 'c',d='d')来调用,注意参数c与参数d
>>> def params_func(a, b, *, c, d):
... print(a+b+c+d)
...
>>> params_func("a",'b',c='c',d='d')
abcd
#这就是不写key的结果
>>> params_func("a",'b','c','d')
Traceback (most recent call last):
File "", line 1, in
TypeError: params_func() takes 2 positional arguments but 4 were given
思考2:是否可以向关键字参数那样使用dict传值
回答: 答案是否定的
>>> dic = {'c':'10', 'd':'ddd'}
#事实证明我们无法使用dict来传值
>>> params_func('a','b', *dic)
Traceback (most recent call last):
File "", line 1, in
TypeError: params_func() takes 2 positional arguments but 4 were given
思考3:使用指定命名关键字参数时,是否还能使用可变参数
回答:答案是否定的
>>> def params_func(a,b,*nums,*,c,d):
File "", line 1
def params_func(a,b,*nums,*,c,d):
^
SyntaxError: invalid syntax
--- 分割线---
>>> def params_func(a,b,*,c,d,*nums):
File "", line 1
def params_func(a,b,*,c,d,*nums):
^
SyntaxError: invalid syntax
思考4:指定命名关键字参数跟关键字参数是否能混用
答案:答案是肯定的
>>> def params_func(a,b,*,c,d,**nums):
... print(a + b + c + d)
... print(nums)
...
>>> params_func('a','b',c='c',d='d',e='e',f='f')
abcd
{'e': 'e', 'f': 'f'}
10.切片
通过切面复制数组
>>> a = [1,2,3,4,5,6]
>>> b = a[:]
>>> b
[1, 2, 3, 4, 5, 6]
通过切面实现数组倒叙
>>> a = [1,2,3,4,5,6]
>>> b = a[::-1]
>>> b
[6, 5, 4, 3, 2, 1]
11.遍历
思考1:遍历数组,同时获取到index ——> 使用enumerate()
In [2]: a = [1,2,3,4]
In [3]: for i, value in enumerate(a):
...: print('{0}, {1}'.format(i,value))
...:
0, 1
1, 2
2, 3
3, 4
思考2:遍历字典,同时有key ,value ——> 使用items()
In [4]: d = {'a': 1, 'b':'bb'}
In [5]: for k,v in d.items():
...: print('{0} , {1}'.format(k, v))
...:
a , 1
b , bb
多说一句 ,如果直接使用d ,获取到的就是keys,与d.keys()一样
12.列表生成器(for for if)
for for if——> 意思是生成器内部一般用for 复杂点就用for for 在复杂点就是 for for if
今天看到一个问题 说怎么把 ("a",2)("b",4) 变为 [{"a": 2} , {"b":4}]
发现可以用列表生成器来做,很是方便
In [6]: t1 = ('a','b','c')
In [7]: t2 = ('d','e','f')
In [17]: arr = [{x:y} for x , y in zip(t1, t2)]
In [20]: for x in arr:
...: print(x)
...:
{'a': 'd'}
{'b': 'e'}
{'c': 'f'}
13.高阶函数
map
对于map等高阶函数的使用还是有些局限
如看到下面这个
list(map(str, [1,2,3,4,5]))
对于str的使用有些疑惑,但是仔细一想,str也是一个函数,也就合理了。
这样说来,只需要谨记两个参数类型即可,第一个为函数,第二个为Iterable。
其实还是对函数作为参数有些不适应,感觉这是一道坎。
reduce
对于reduce参数,跟map很相似,一个函数一个Iterable,但是不同的是,reduce的函数必须是 两个参数。 其原因就是,他会将Iterable内的数据两个两个的进行操作,然后并将上一次的操作结果作为下一次的第一个数据。
filter
跟map/reduce很相似,都是一个函数一个Iterable,不同的是函数的返回值需要一个Bool类型,来进行筛选那些值需要留下,那些值不需要留下
>>> def fn(nums):
... return filter(lambda x:x > 0, map(lambda x:x - 10 * 3, nums))
...
>>> list(fn([2,3,5,66,55,44,29]))
[36, 25, 14]
14.匿名函数
匿名函数的限制,只能有一个表达是,不用写return。
匿名函数也是一个函数对象,可以把匿名函数复制给一个变量
思考1 匿名函数作为返回值
In [12]: def haha(x ,y):
...: return lambda: x * x + y * y
...:
In [14]: f = haha(10 , 20)
In [15]: f()
Out[15]: 500
思考2 匿名函数作为返回值,同时需要输入参数
In [16]: def haha():
...: return lambda x, y:x * x + y *y
...:
In [17]: f = haha()
In [18]: f(10,20)
Out[18]: 500
15.类
15.1 多态
这里只能说是关于多态的一些思考。
何为多态,我的理解是同一行为的不同表现。同一行为也就是某种动作,而在类中,动作就意味着是方法。同一行为说明,大家都有这种行为,而不同表现,则是对这种行为的不同表现。
15.2 关于方法的动态绑定
对于这种方法已经了解,但是对于这种方法的使用场景并没有想到,并不知道怎么使用。
思考1 如果在绑定函数前先创建一个对象,再绑定函数再创建一个对象,是否前一个对象会是绑定前的状态呢。
In [57]: def dynamic_func(self, name):
...: print('just print some {0}'.format(name))
...:
#绑定前创建对象
In [58]: b = Car()
In [59]: Car.dynamic_func = dynamic_func
#绑定后创建对象
In [60]: c = Car()
#调用b对象的动态方法
In [63]: b.dynamic_func('haha')
just print some haha
#调用c对象的动态方法
In [64]: c.dynamic_func('haha')
just print some haha
再看一个
In [65]: b = Bike()
In [66]: b.dynamic_func('haha')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
----> 1 b.dynamic_func('haha')
AttributeError: 'Bike' object has no attribute 'dynamic_func'
In [67]: Bike.dynamic_func = dynamic_func
In [68]: b.dynamic_func('haha')
just print some haha
这果然很动态.
16.关于进程与多线程
进程与多线程一直都是python的一个痛点,现在的我还感受不到痛点,因为技术不够。
16.1关于进程
multiprocessing 下的 Process 使用也很简单
只需要给Process指定一个target方法, 在Process在启动时会去执行指定的那个target方法,同时也会开启新的进程,所以python的进程就这么简单。实现大概是这样。
#这就是那个target方法
In [36]: def process_def(name):
...: print('do some thing with process name:{0}'.format(name))
...: print('current process {0}'.format(os.getpid()))
...: print('sleep 3 second')
...: time.sleep(3)
...:
#执行进程的方法
In [38]: def do_process():
#创建进程
...: p = Process(target=process_def, args=('new_process',))
#打印当前进程,以做比较
...: print('current process {0}'.format(os.getpid()))
#开始进程前
...: print('process before start')
#开启进程
...: p.start()
...: p.join() #表示*调用join方法的那个进程*执行完成后,才会在继续执行当前线程 **内的内容很关键
#线程结束
...: print('process end')
...:
#执行方法
In [39]: dp_process()
#不难发现执行前的线程id是2511
current process 2511
process before start
do some thing with process name:new_process
#启动的进程id是3426
current process 3426
sleep 3 second
process end
16.2关于多个进程操作
多个进程就是用Pool来操作就可以了 也是在multiprocessing中包含的。
In [53]: from multiprocessing import Pool
In [54]: import time, os, random
#按照规定一样有一个target process def
In [55]: def test_task(name):
...: print('Run task {0} ({1})'.format(name, os.getpid()))
...: start = time.time()
...: time.sleep(random.random() * 3)
...: end = time.time()
...: print('Task {0} runs {1} seconds'.format(name, end-start))
...:
In [56]: def do_task():
...: print('current process {0}'.format(os.getpid()))
#这里创建一个Pool
...: p = Pool(4)
...: for i in range(5):
#只需要调用apply_async来启动异步进程即可
...: p.apply_async(test_task, args=(i,))
...: print('before start')
...: p.close()
...: p.join()
...: print('end')
...:
In [57]: do_task()
current process 2511
before start
Run task 0 (3484)
Run task 1 (3485)
Run task 2 (3486)
Run task 3 (3487)
Task 0 runs 0.260403871536 seconds
Run task 4 (3484)
Task 1 runs 1.60321497917 seconds
Task 4 runs 1.82880997658 seconds
Task 2 runs 2.11483192444 seconds
Task 3 runs 2.80249190331 seconds
end
16.3关于多线程,其实与多进程很像,都需要一个指定的def
#需要制定的thread def
In [10]: def test_thread():
...: print('thread {0} is running...'.format(threading.current_thread().
...: name))
...: time.sleep(2)
...: print('end {0}'.format(threading.current_thread().name))
...:
In [11]: def do_thread():
...: print('before start {0}'.format(threading.current_thread().name))
#核心在这里 threading.Thread(target, 这需要制定一个name) target还是指向到了test_thread
...: t = threading.Thread(target=test_thread, name='LoopThread')
...: t.start()
...: t.join()
...: print('end{0}'.format(threading.current_thread().name))
...:
In [12]: do_thread()
before start MainThread
thread LoopThread is running...
end LoopThread
endMainThread
16.4 Pool.close
昨天看文章时,看到有一个close,但是没有看清楚这个close是谁调用的,还以为所有的Process 与 Thread都可以调用,于是今天早上调用了半天的close才发现Process 与 Thread 都没有close,如下:
#Thread中 使用dir查看属性 并没有发现close
>>> import threading
>>> t = threading.Thread()
>>> t.close()
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'Thread' object has no attribute 'close'
>>> dir(t)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_args', '_bootstrap', '_bootstrap_inner', '_daemonic', '_delete', '_exc_info', '_ident', '_initialized', '_is_stopped', '_kwargs', '_name', '_reset_internal_locks', '_set_ident', '_set_tstate_lock', '_started', '_stderr', '_stop', '_target', '_tstate_lock', '_wait_for_tstate_lock', 'daemon', 'getName', 'ident', 'isAlive', 'isDaemon', 'is_alive', 'join', 'name', 'run', 'setDaemon', 'setName', 'start']
#Process中 使用dir查看同样没有close
>>> from multiprocessing import Process
>>> def test_close():
... pass
...
>>> p = Process(target=test_close)
>>> p.close()
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'Process' object has no attribute 'close'
>>> dir(p)
['_Popen', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_args', '_bootstrap', '_config', '_identity', '_kwargs', '_name', '_parent_pid', '_popen', '_start_method', '_target', 'authkey', 'daemon', 'exitcode', 'ident', 'is_alive', 'join', 'name', 'pid', 'run', 'sentinel', 'start', 'terminate']
问题来了,这个close是哪里的呢,找了半天才发现,这个close是Pool的,哎,真的是自己不仔细,不过也好,印象深刻了。关于close的使用。
close的作用:使用p.close后,标记着Pool关闭,其他的进程就不会再进入。
16.5 看分布式进程——队列新悟
之前看到队列一次很是抵触,因为大学期间数据结构刚及格,对结构类型都很抵触,不过今天看到分布式进程这里,发现队列的使用其实就是符合队列自身的特性的——先进先出。
这里使用QueueManger来进行管理。
因为是分布式, 要在多台机子上共享,于是有一台机子应该是做任务发布,其他机子都是任务执行者,将执行结果返回给任务发布这台机子。
发布机子的代码:
1.注册方法
QueueManager.register('get_task_queue', callable=lambda: task_queue)
QueueManager.register('get_result_queue', callable=lambda: result_queue)
2.创建 / 启动manager
#这里需要说明一下:因为要共享,于是这里就需要指定一个端口。
manager = QueueManager(address=('', 5000), authkey=b'abc')
manager.start()
3.获取task队列 获取result队列
task = manager.get_task_queue()
result = manager.get_result_queue()
4.模拟任务发布
for i in range(10):
n = random.randint(0,1000)
print('put task {0}'.format(n))
#核心在这 ,将n put到task队列中
task.put(n)
5.模拟结果获取
for i in range(10):
#核心在这,通过get在result队列中取出数据
r = result.get(timeout= 10)
print('Result: %s' % r)
任务执行
1.一样注册方法
#我们不需要填写回调值,因为我们会直接使用QueueManager中的值
QueueManger.register('get_task_queue')
QueueManger.register('get_result_queue')
2.创建 / 链接manager 创建并链接
m = QueueManger(address=(server_addr, 5000), authkey=b'abc')
m.connect()
3.获取task / result队列
task = m.get_task_queue()
result = m.get_result_queue()
- 获取任务值,同时执行任务,同时返回到结果队列中
for i in range(10):
try:
#核心, 获取到任务值
n = task.get(timeout=1)
print('run task %d * %d...' % (n, n))
#模拟执行任务
r = '%d * %d = %d' % (n,n, n *n)
time.sleep(1)
#放入队列中,这里放入后,发布任务机子就会获取到任务
result.put(r)
except Queue.Empty:
print('task queue is empty.')
分布式参考文章
17 三目运算符
其他语言的三目运算符一般是
a = 50 > 100 ? 'right' : 'left'
但是看完python的后我惊呆了
a = 'right' if 50 > 100 else 'left'
这种还算好理解,如果为真就是'right',如果为假就为'left'
但是有一个很恐怖写法的就是
a = ('left' ,'right')[50 > 100]
这是啥?谁能给我解释解释,翻译一下是,如果[]内为真就返回'right',否则就返回'left'。
尤其需要注意的是Tuple内,tuple[0]一个代表False要返回的值,tuple[1]代表True返回的值
伪代码就是这样的(if_false_return , if_true_return)[condition_false_or_true]
三目参考文章
18.修饰器
修饰器这个词,很早就已经知道了,一直都不是很理解,到现在也只能说说自己的一点理解,无法说的明白。
18.1 理解与使用
修饰器的核心可以理解为fn = decorator(fn),也就是decorator函数,参数为一个函数,返回值还是一个函数,而且传入的函数接收返回的函数,看个例子。
#创建我们的decorator
In [72]: def my_decorator(fn):
...: def wrapper():
...: print('this is in wrapper')
#在这里调用fn,听说这样可以给fn添加功能,同时不会破坏fn自身代码的完整性
...: fn()
...: print('this will leave wrapper')
...: return wrapper
...:
#创建我们的function
In [73]: def my_fn():
...: print('this is print in my_fn')
...:
这两个创建完成,剩下的就是使用了。不适用decorator的情况下
#my_decorator接受传入的函数my_fn,my_fn同时接受返回的函数
In [74]: my_fn = my_decorator(my_fn)
#使用一下,新返回的my_fn函数
In [75]: my_fn()
this is in wrapper
this is print in my_fn
this will leave wrapper
我们可以看到my_fn已经被扩充了。
修饰器是什么呢,修饰器就是我们来省略 fn = decorator(fn)这句话的一个方式。
使用decorator后(请看清这里代码与上面代码的不同)
#对于decorator,不需要有任何的改变
In [72]: def my_decorator(fn):
...: def wrapper():
...: print('this is in wrapper')
#在这里调用fn,听说这样可以给fn添加功能,同时不会破坏fn自身代码的完整性
...: fn()
...: print('this will leave wrapper')
...: return wrapper
...:
#而在编写my_decorator的时候,我们需要添加@my_decorator来标记
In [76]: @my_decorator
...: def my_fn():
...: print('this is my_fn')
...:
#不难发现, 我们不使用 fn= decorator(fn) 也能直接使用my_fn了
In [77]: my_fn()
this is in wrapper
this is my_fn
this will leave wrapper
而这就是修饰器的核心。
18.2 修饰器进化,带参数修饰器
带参数修饰器本质跟修饰器一样,不同的是带参数,这句话好像没说 0-0,直接来看一下吧。
一般修饰器 fn = decorator(fn)
带参修饰器 fn = decorator(*args, **kwds)(fn)
而这里的核心就是,为了确保满足fn = decorator(fn)这一条件,decorator(*args, **kwds)应该返回的是一个decorator。于是有了下面这段代码
"""
首先要知道的是 makeHtmlTag最终返回的是real_decorator 这意味着返回的是一个修饰器
其次这里我们要注意makeHtmlTag的可变参数时args kwds,而里面的wrapper的可变参数时
"""
In [78]: def makeHtmlTag(tag, *args, **kwds):
...: def real_decorator(fn):
...: css_class= "class='{0}'".format(kwds["css_class"]) if "css_clas
...: s" in kwds else ""
#看这里,参数是 arg 与 kwd
...: def wrapper(*arg, **kwd):
...: return "<"+tag+css_class+">" + fn(*arg, **kwd)+"<"+tag+">"
...: return wrapper
...: return real_decorator
...:
这里你可能会有疑问,为什么wrapper与makeHtmlTag的参数会不一样,很简单,因为wrapper是作为fn的返回值,makeHtmlTag的参数则是为real_decorator服务的。
于是我们可以这样用。
#定义hello函数
In [82]: @makeHtmlTag(tag='b', css_class='bold_css')
...: def hello(*arg, **kwd):
...: return 'hello world '+kwd['name']
...:
#使用hello函数,我们要明确这里的hello函数不再是之前的hello函数了,而是返回来的wrapper了
In [83]: print(hello(name='whale'))
hello world whale
为了证明第二行注释的结论我们来做两个实验。
实验1
我们来修改一下修饰器
In [84]: def makeHtmlTag(tag, *args, **kwds):
...: def real_decorator(fn):
...: css_class= "class='{0}'".format(kwds["css_class"]) if "css_clas
...: s" in kwds else ""
#注意加注释的这里,这里没有个wrapper
...: def wrapper():
...: return "<"+tag+css_class+">" + fn()+"<"+tag+">"
...: return wrapper
...: return real_decorator
...:
再用之前的写法看看是否可以调用的通
In [85]: @makeHtmlTag(tag='b', css_class='bold_css')
...: def hello(*arg, **kwd):
...: return 'hello world '+kwd['name']
...:
In [86]: print(hello(name='whale'))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 print(hello(name='whale'))
TypeError: wrapper() takes no arguments (1 given)
我们来看一下报错TypeError: wrapper() takes no arguments (1 given) wrapper()没有参数但是我们给了一个,我们明明给hello赋值,为什么说wrapper没有参数呢,因为在修饰器里面返回的就是wrapper。
实验2
恢复正常,我们在来看一下hello的__name__
In [97]: @makeHtmlTag(tag='b', css_class='bold_css')
...: def hello(*arg, **kwd):
...: return 'hello world '+kwd['name']
...:
In [99]: hello.__name__
Out[99]: 'wrapper'
我们不难看出。虽然我们调用的hello但是其实已经变成了wrapper。
18.3 关于wraps
wraps是用来避免返回函数不同而引发的问题,虽然这也不能完全解决所有问题,但是大多数情况下还是够用的。
引入wraps
In [101]: from functools import wraps
使用wraps
In [102]: def makeHtmlTag(tag, *args, **kwds):
...: def real_decorator(fn):
...: css_class= "class='{0}'".format(kwds["css_class"]) if "css_cla
...: ss" in kwds else ""
...: @wraps(fn)
...: def wrapper(*arg, **kwd):
...: return "<"+tag+css_class+">" + fn(*arg, **kwd)+"<"+tag+">"
...:
...: return wrapper
...: return real_decorator
...:
查看结果
In [103]: @makeHtmlTag(tag='b', css_class='bold_css')
...: def hello(*arg, **kwd):
...: return 'hello world '+kwd['name']
...:
In [104]: hello.__name__
Out[104]: 'hello'