python基础学习
Posted yongfuxue
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python基础学习相关的知识,希望对你有一定的参考价值。
递归
递归的经典例子
def fib(n):
if n < 1:
raise ValueError
elif (n == 1) or (n == 2):
return 1
else:
return fib(n - 1) + fib(n - 2)
print(fib(1)) #1
print(fib(2)) #1
print(fib(3)) #2
print(fib(4)) #3
print(fib(5)) #5
print(fib(6)) #8
汉诺塔问题
def move(n, source, target, helper):
if n == 1:
print (source + ‘ -> ‘ + target)
else:
move(n - 1, source, helper, target)
print (source + ‘ -> ‘ + target)
move(n - 1, helper, target, source)
move(4, ‘A‘, ‘B‘, ‘C‘)
迭代器与可迭代对象
定义:
list,truple,str这些都是可迭代对象,但是他们不一定是迭代器。迭代器本身不知道自己要执行多少次,所以可以理解为不知道有多少个元素,每调用一次next(),就会往下走一步,是惰性的。
迭代器提供了一种不依赖索引取值的方式,这样可以遍历没有索引的可迭代对象,比如字典、集合、文件等等,加载这一个元素至内存中随后释放,相比之下更节省内存,但是我们没有办法获取迭代器的长度,而且只能往后依次取值。
只要对象本身有__iter__方法,那它就是可以迭代的。
d={‘a‘:1,‘b‘:2,‘c‘:3}
d.__iter__()
执行对象下的__iter__方法得到的就是迭代器
d={‘a‘:1,‘b‘:2,‘c‘:3}
a=d.__iter__()
print(type(a))
print(next(a))
print(next(a))
使用迭代器:
使用内建的工厂函数iter(iterable)可以获取迭代器对象
>>> lst = range(5)
>>> it = iter(lst)
>>> it
<listiterator object at 0x01A63110>
使用next()方法可以访问下一个元素
因为迭代器如此普遍,python专门为for关键字做了迭代器的语法糖。在for循环中,Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,还完成了检查StopIteration异常的工作。如下
>>> a = (1, 2, 3, 4)
>>> for key in a:
print key
首先python对关键字in后的对象调用iter函数迭代器,然后调用迭代器的next方法获得元素,直到抛出StopIteration异常。
自定义一个迭代器:
class Fibs:
def __init__(self): #初始化
self.a = 0
self.b = 1
def __next__(self): #获取下一个条目
self.a, self.b = self.b, self.a + self.b
return self.a
def __iter__(self): #返回迭代器
return self
fibs = Fibs() #产生一个对象
for f in fibs: #迭代
if f > 100:
break
print(f)
字符反转,常见面试题
def reverse(str_list, start, end):
while start < end:
str_list[start], str_list[end] = str_list[end], str_list[start]
start += 1
end -= 1
setence = ‘ Hello, how are you? Fine. ‘
str_list = list(setence)
i = 0
while i < len(str_list):
if str_list[i] != ‘ ‘:
start = i
end = start + 1
while (end < len(str_list)) and str_list[end] != ‘ ‘:
end += 1
reverse(str_list, start, end - 1)
i = end
else:
i += 1
str_list.reverse()
print(‘‘.join(str_list))
>> Fine. you? are how Hello,
面向对象
1)self
类中的函数第一个参数必须是self,类中定义的函数叫做“方法”。
self 是个什么鬼呢?它是为了指代它所存在的类Class之中。
比如我们如果有好几个不同的obj被创建成同一个类,
那么有了self,我们的class Foo就能很好的知道哪个指的是自己,不会乱
所以说,这个 self 就是个代指。代指了自己所在的class。你可以由 self 点进所指class本身的函数。由此可见,self 本身作为一个代词,并不一定要叫self。你也可以用个其他什么来代替。只不过,必须得是这个类的所有子方法的第一个参数:
2)访问控制
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问
class Student:
def __init__(self, name, age):
self.__name = name
self.__age = age
def detail(self):
print(self.__name)
print(self.__age)
LiLei = Student(‘LiLei‘, 12)
LiLei.__age = 20
LiLei.detail()
>>
LiLei
12
3)一些方法
Dir(): 如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法
getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态:
hasattr(obj, ‘x‘) #有木有属性‘x‘
setattr(obj, ‘y‘, 19) # 设置一个属性‘y‘
getattr(obj, ‘y‘) # 获取属性‘y‘
getattr(obj, ‘z‘, 404) # 获取属性‘z‘,如果不存在,返回默认值404
4)实例属性和类属性
实例属性:
class Student(object):
def __init__(self, name):
self.name = name
s = Student(‘Bob‘)
s.score = 90
类属性:
class Student(object):
name = ‘Student‘
s = Student() # 创建实例s
print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
s.name = ‘Michael‘ # 给实例绑定name属性
print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
总结:
从上面的例子可以看出,在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
5)super()
文件读写
1) 直接读入
read()将文本文件所有行读到一个字符串中。
readline()是一行一行的读
readlines()是将文本文件中所有行读到一个list中,文本文件每一行是list的一个元素。优点:readline()可以在读行过程中跳过特定行。
2) 文件迭代器,用for循环的方法
file = open("output.txt","w")
for line in open("test.txt"):
#这里可以进行逻辑处理
file.write(‘"‘+line[:s]+‘"‘+",")
3)文件上下文管理器
with open(‘somefile.txt‘, ‘r‘) as f:
4)二进制文件读写
f = open(‘DegangGuo.txt‘, ‘rb‘)
# 读入郭德纲老师的作文, 但是郭老师用的是参合着错别字的繁体编码,假设叫个"DeyunCode"
# 那么你读入以后,就需要解码它
u = f.read().decode(‘DeyunCode‘)
文件目录操作
os.path.join(‘/Users/EDC‘, ‘Pictures‘)
# 这里你得到的是一个字符串,代表了新的文件夹是这个位置:/Users/EDC/Pictures/
# 自己也可以拼起来,但是怕不同操作系统下的区分符问题,最好是用OS接口
# 但是你还并没有创建任何的文件
# 需要用mkdir创建:
os.mkdir(‘/Users/EDC/Pictures/‘)
# 同理,删除一个文件夹
os.rmdir(‘/Users/EDC/Pictures/‘)
os.path.split(‘/Users/EDC/Pictures/AJiao.avi‘)
# (‘/Users/EDC/Pictures/‘, ‘AJiao.avi‘)
os.path.splitext(‘/Users/EDC/Pictures/AJiao.avi‘)
# (‘/Users/EDC/Pictures/AJiao‘, ‘.avi‘)
#复制文件
import shutil
shutil.copyfile(‘/path/to/file‘, ‘/path/to/other/file‘)
匿名函数
python 使用 lambda 来创建匿名函数。
lambda只是一个表达式,函数体比def简单很多。
lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
语法:lambda [arg1 [,arg2,.....argn]]:expression
Reduce:
传给reduce中的函数func() (必须是一个二元操作函数)先对集合中的第1,2个数据进行操作,得到的结果再与第三个数据用func()函数运算,最后得到一个结果。
from functools import reduce
l = [1,2,3,4,5]
print(reduce(lambda x,y: x+y, l))
# 这里代表着,把list中的值,一个个放进lamda的x,y中
# 如果你给出一个初始值,可以放在list后面
print(reduce(lambda x,y: x+y, l, 10))
# 这样,x开始的时候被赋值为10,然后依次
Map:
格式:map(func, seq1[, seq2...] )
Python函数式编程中的map()函数是将func作用于seq中的每一个元素,并用一个列表给出返回值。
l = [1,2,3]
new_list = list(map(lambda i: i+1, l))
print(new_list)
# Py3里,外面需要套个list:
# 这是为了让里面的值给显示出来,要不然你会得到这是个map函数
# 而不是里面的值。
# Py2的童鞋不虚
# 我们也可以把两个数组搞成一个单独的数组
l2 = [4,5,6]
new_list = list(map(lambda x,y: x+y, l, l2))
print(new_list)
[2, 3, 4]
[5, 7, 9]
Filter:
和map()类似,filter()也接收一个函数和一个序列。和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
l = [100, 20, 24, 50, 110]
new = list(filter(lambda x: x<50, l))
# 同理,py3得套个list来转化成list函数,便于打印出来
print(new) [20, 24]
进程和线程
概念梳理
进程:
一个程序的执行实例就是一个进程。每一个进程提供执行程序所需的所有资源。(进程本质上是资源的集合)
每个进程有自己的内存空间、数据栈等,只能使用进程间通讯,而不能直接共享信息。
线程:
所有线程运行在同一个进程中,共享相同的运行环境。
每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。每条线程可以执行多个任务。
线程的运行可以被抢占(中断),或暂时被挂起(睡眠),让其他线程运行(让步)。
一个进程中的各个线程间共享同一片数据空间。
进程和线程的区别:
1.同一个进程中的线程共享同一内存空间,但是进程之间是独立的。
2.同一个进程中的所有线程的数据是共享的(进程通讯),进程之间的数据是独立的。
3.对主线程的修改可能会影响其他线程的行为,但是父进程的修改(除了删除以外)不会影响其他子进程。
4.线程是一个上下文的执行指令,而进程则是与运算相关的一簇资源。
5.同一个进程的线程之间可以直接通信,但是进程之间的交流需要借助中间代理来实现。
6.创建新的线程很容易,但是创建新的进程需要对父进程做一次复制。
7.一个线程可以操作同一进程的其他线程,但是进程只能操作其子进程。
8.线程启动速度快,进程启动速度慢(但是两者运行速度没有可比性)。
多线程
常用方法:
start() 线程准备就绪,等待CPU调度
setName() 为线程设置名称
getName() 获取线程名称
setDaemon(True) 设置为守护线程, 使用setDaemon(True)把所有的子线程都变成了主线程的守护线程,因此当主进程结束后,子线程也会随之结束。所以当主线程结束后,整个程序就退出了。
join() 逐个执行每个线程,执行完毕后继续往下执行
run() 线程被cpu调度后自动执行线程对象的run方法,如果想自定义线程类,直接重写run方法就行了
GIL:
在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中,无论有多少核,同时只能执行一个线程。究其原因,这就是由于GIL的存在导致的。
GIL是一把全局排他锁,同一时刻只有一个线程在运行。毫无疑问全局锁的存在会对多线程的效率有不小影响。甚至就几乎等于Python是个单线程的程序。
multiprocessing库的出现很大程度上是为了弥补thread库因为GIL而低效的缺陷。它完整的复制了一套thread所提供的接口方
便迁移。唯一的不同就是它使用了多进程而不是多线程。每个进程有自己的独立的GIL,因此也不会出现进程之间的GIL争
抢。
线程锁和互斥锁和递归锁和信号量:
1)由于线程之间是进行随机调度,如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期,我们也称此为“线程不安全”。
2)为了方式上面情况的发生,就出现了互斥锁(Lock)。
3)RLcok类的用法和Lock类一模一样,但它支持嵌套,,在多个锁没有释放的时候一般会使用使用RLcok类。
4)互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据
def run(n):
lock.acquire() #获取锁
global num
num += 1
lock.release() #释放锁
lock = threading.Lock() #实例化一个锁对象
num = 0
t_obj = []
for i in range(20000):
t = threading.Thread(target=run, args=("t-%s" % i,))
t.start()
t_obj.append(t)
for t in t_obj:
t.join()
print "num:", num
信号量
def run(n):
semaphore.acquire() #加锁
time.sleep(1)
print("run the thread:%s " % n)
semaphore.release() #释放
semaphore = threading.BoundedSemaphore(5) # 最多允许5个线程同时运行
事件(Event类)
python线程的事件用于主线程控制其他线程的执行,事件是一个简单的线程同步对象。
事件处理的机制:全局定义了一个“Flag”,当flag值为“False”,那么event.wait()就会阻塞,当flag值为“True”,那么event.wait()便不再阻塞。
#利用Event类模拟红绿灯
import threading
import time
event = threading.Event()
def lighter():
count = 0
event.set() #初始值为绿灯
while True:
if 5 < count <=10 :
event.clear() # 红灯,清除标志位
print("33[41;1mred light is on... 33[0m")
elif count > 10:
event.set() # 绿灯,设置标志位
count = 0
else:
print("33[42;1mgreen light is on... 33[0m")
time.sleep(1)
count += 1
def car(name):
while True:
if event.is_set(): #判断是否设置了标志位
print("[%s] running..."%name)
time.sleep(1)
else:
print("[%s] sees red light,waiting..."%name)
event.wait()
print("[%s] green light is on,start going..."%name)
light = threading.Thread(target=lighter,)
light.start()
car = threading.Thread(target=car,args=("MINI",))
car.start()
定时器
定时器,指定n秒后执行某操作
from threading import Timer
def hello():
print("hello, world")
t = Timer(1, hello)
t.start() # after 1 seconds, "hello, world" will be printed
多进程
在linux中,每个进程都是由父进程提供的。每启动一个子进程就从父进程克隆一份数据,但是进程之间的数据本身是不能共享的。
多进程
进程间通信
1)queue():
from multiprocessing import Process, Queue
import time
def write(q):
for i in [‘A‘,‘B‘,‘C‘,‘D‘,‘E‘]:
print(‘Put %s to queue‘ % i)
q.put(i)
time.sleep(0.5)
def read(q):
while True:
v = q.get(True)
print(‘get %s from queue‘ %v)
if(v == ‘E‘): break;
if __name__ == ‘__main__‘:
q = Queue()
pw = Process(target=write,args=(q,))
pr = Process(target=read,args=(q,))
pw.start()
pr.start()
pr.join()
pr.terminate()
2)pipe():
Pipe的本质是进程之间的数据传递,而不是数据共享,这和socket有点像。pipe()返回两个连接对象分别表示管道的两端,每端都有send()和recv()方法。
from multiprocessing import Process, Pipe
def f(conn):
conn.send([42, None, ‘hello‘])
conn.close()
if __name__ == ‘__main__‘:
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "[42, None, ‘hello‘]"
p.join()
进程锁
数据输出的时候保证不同进程的输出内容在同一块屏幕正常显示,防止数据乱序的情况。
from multiprocessing import Process, Lock
def f(l, i):
l.acquire()
try:
print(‘hello world‘, i)
finally:
l.release()
if __name__ == ‘__main__‘:
lock = Lock()
for num in range(10):
Process(target=f, args=(lock, num)).start()
进程池
由于进程启动的开销比较大,使用多进程的时候会导致大量内存空间被消耗。为了防止这种情况发生可以使用进程池,(由于启动线程的开销比较小,所以不需要线程池这种概念,多线程只会频繁得切换cpu导致系统变慢,并不会占用过多的内存空间)
进程池中常用方法:
apply() 同步执行(串行)
apply_async() 异步执行(并行)
terminate() 立刻关闭进程池
join() 主进程等待所有子进程执行完毕。必须在close或terminate()之后。
close() 等待所有进程结束后,才关闭进程池。
进程池内部维护一个进程序列,当使用时,去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。在上面的程序中产生了10个进程,但是只能有5同时被放入进程池,剩下的都被暂时挂起,并不占用内存空间,等前面的五个进程执行完后,再执行剩下5个进程。
from multiprocessing import Pool
import time
def f(x):
print x*x
time.sleep(2)
return x*x
if __name__ == ‘__main__‘:
‘‘‘定义启动的进程数量‘‘‘
pool = Pool(processes=5)
res_list = []
for i in range(10):
‘‘‘以异步并行的方式启动进程,如果要同步等待的方式,可以在每次启动进程之后调用res.get()方法,也可以使用Pool.apply‘‘‘
res = pool.apply_async(f,[i,])
print(‘-------:‘,i)
res_list.append(res)
pool.close()
pool.join()
for r in res_list:
print "result",(r.get(timeout=5))
>>>
0
4
1
9
16
(‘-------:‘, 0)
(‘-------:‘, 1)
(‘-------:‘, 2)
(‘-------:‘, 3)
(‘-------:‘, 4)
(‘-------:‘, 5)
(‘-------:‘, 6)
(‘-------:‘, 7)
(‘-------:‘, 8)
(‘-------:‘, 9)
25
36
49
64
81
result 0
result 1
result 4
result 9
result 16
result 25
result 36
result 49
result 64
result 81
魔术方法
1. __doc__
表示类的描述信息
class Foo:
""" 描述类信息 """
def func(self):
pass
print(Foo.__doc__)
#输出:类的描述信息
2. __module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
test.py文件
# -*- coding:utf-8 -*-
class Person(object):
def __init__(self):
self.name = ‘laowang‘
main.py文件
from test import Person
obj = Person()
print(obj.__module__) # 输出 test 即:输出模块
print(obj.__class__) # 输出 test.Person 即:输出类
3. __del__
当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,__del__的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo:
def __del__(self):
pass
4. __call__
对象后面加括号,触发执行。
注:__init__方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print(‘__call__‘)
obj = Foo() # 执行 __init__
obj() # 执行 __call__
5. __dict__
类或对象中的所有属性
类的实例属性属于对象;类中的类属性和方法等属于类,即:
class Province(object):
country = ‘China‘
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print(‘func‘)
# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
# 输出:{‘__dict__‘: <attribute ‘__dict__‘ of ‘Province‘ objects>, ‘__module__‘: ‘__main__‘, ‘country‘: ‘China‘, ‘__doc__‘: None, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Province‘ objects>, ‘func‘: <function Province.func at 0x101897950>, ‘__init__‘: <function Province.__init__ at 0x1018978c8>}
obj1 = Province(‘山东‘, 10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出:{‘count‘: 10000, ‘name‘: ‘山东‘}
obj2 = Province(‘山西‘, 20000)
print(obj2.__dict__)
# 获取 对象obj1 的属性
# 输出:{‘count‘: 20000, ‘name‘: ‘山西‘}
6.魔术方法之运算方法
举例:__add__
class Rectangle(object):
def __init__(self, length, width):
self.length = length
self.width = width
def get_area(self):
return self.length * self.width
def __add__(self, other): #__add__魔术方法,相加
add_length = self.length + other.length
add_width = self.width + other.width
return add_length, add_width
rec1 = Rectangle(10,8)
rec2 = Rectangle(20,15)
print(rec1 + rec2)
运行结果:
(30, 23)
到底是怎么回事呢?
>>> a = 1
>>> b =2
>>> a + b
3
>>> b.__add__(a) #实际a + b 就等同于 b.__add__(a) 这里a, b均是两个类
3
魔术方法之运算方法:
__add__(self,other) # x+y
__sub__(self,other) # x-y
__mul__(self,other) # x*y
__mod__(self,other) # x%y
__iadd__(self,other) # x+=y
__isub__(self,other) # x-=y
__radd__(self,other) # y+x
__rsub__(self,other) # y-x
__imul__(self,other) # x*=y
__imod__(self,other) # x%=y
7. __str__
如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class Foo:
def __str__(self):
return ‘laowang‘
obj = Foo()
print(obj)
# 输出:laowang
8、__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据
# -*- coding:utf-8 -*-
class Foo(object):
def __getitem__(self, key):
print(‘__getitem__‘, key)
def __setitem__(self, key, value):
print(‘__setitem__‘, key, value)
def __delitem__(self, key):
print(‘__delitem__‘, key)
obj = Foo()
result = obj[‘k1‘] # 自动触发执行 __getitem__
obj[‘k2‘] = ‘laowang‘ # 自动触发执行 __setitem__
del obj[‘k1‘] # 自动触发执行 __delitem__
9、__getslice__、__setslice__、__delslice__
该三个方法用于分片操作,如:列表
# -*- coding:utf-8 -*-
class Foo(object):
def __getslice__(self, i, j):
print(‘__getslice__‘, i, j)
def __setslice__(self, i, j, sequence):
print(‘__setslice__‘, i, j)
def __delslice__(self, i, j):
print(‘__delslice__‘, i, j)
obj = Foo()
obj[-1:1] # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__
del obj[0:2] # 自动触发执行 __delslice__
数组List的操作
a = [1,2,3,4,5,6,7]
b = [11,22,33]
a[ : 1] = b
则a = [11,22,33,2,3,4,5,6,7]
以上是关于python基础学习的主要内容,如果未能解决你的问题,请参考以下文章