python拓展知识

类方法self的作用

未绑定的类方法:没有self,通过类来引用方法返回一个未绑定方法对象。要调用它,你必须显示地提供一个实例作为第一个参数。

绑定的实例方法:有self,通过实例访问方法返回一个绑定的方法对象。Python自动地给方法绑定一个实例,所以我们调用它时不用再传一个实例参数。

class Test(object):

    def func(self, message):
        print message


obj = Test()
x = obj.func
# x 是一个绑定的方法对象, 不需要传递一个实例参数
x('hi')

# fn 是一个未绑定的方法对象, 需要传递一个实例参数
fn = Test.func
fn(obj, 'hi')

# fn('hi') # 错误的调用

type关键字

既可以查看一个对象的类型 ,也可以创建类对象

查看对象类型

a = 1
type(a)
# <type 'int'>

b = 'hello'
type(b)
# <type 'str'>

 

创建类对象

# 需要给一个参数来绑定类
def fn(self):
    print 'hello world'

Hello = type('Hello', (object,), dict(hello=fn))

h = Hello()
h.hello()

 

locals() 和 globals() 的区别

locals()返回局部所有类型的变量的字典, globals()返回当前位置的全部全局变量的字典

def test(arg):
    z = 1
    print locals()

test(4)
# {'z': 1, 'arg': 4}
print globals()
# {'__builtins__': <module '__builtin__' (built-in)>, '__file__': '...', '__package__': None, 'test': <function test at 0x10ea970c8>, '__name__': '__main__', '__doc__': None}

 

字符串title函数

将字符串的每个单词的首字母大写,下划线连接的单词首字母也要大写

str = 'my_str'
print str.title()
# My_Str

 

字符串的rpartition函数

以某个分隔符将字符串分成两段,取最后一个匹配的位置的分隔符分隔,返回一个三元组(head, sep, tail)

print 'django.core.app'.rpartition('.')
# ('django.core', '.', 'app')

python特殊函数__call__

所有的函数都是可调用对象,一个类实例也可以变成一个可调用对象,只需实现一个特殊方法__call__()

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def __call__(self, job):
        print 'My name is %s' % self.name
        print 'My gender is %s' % self.gender
        print 'My job is %s' % job

p = Person('Bob', 'male')
p('student')

python strip(), lstrip(), rstrip()函数

strip(): 返回字符串的副本,并删除前导和后缀字符

lstrip(): 返回字符串的副本,删除前导字符

rstrip(): 返回字符串的副本,删除后缀字符

str = ' hello '
print len(str)  # 5
print len(str.strip())  # 7

str = 'rhellor'
print str.strip('r')  # hello
print str.lstrip('r')  # hellor
print str.rstrip('r')  # rhello

random.shuffle()

random.shuffle 将列表随机排序

import random

li = [1, 2, 3, 4, 5]
random.shuffle(li)

for i in li:
    print i

 os.path模块

os.path.basename(path):获取对应路径下的文件名

os.path.abspath(path):返回path规范化的绝对路径

os.path.split(path): 将path分割成目录和文件名二元组返回

os.path.dirname(path): 返回path的目录

os.path.exists(path): 如果path存在,返回True;如果path不存在,返回False

os.path.isabs(path): 如果path是绝对路径,返回True

os.path.isfile(path): 如果path是一个存在的文件,返回True。否则返回False

os.path.isdir(path): 如果path是一个存在的目录,则返回True。否则返回False

os.path.splitext(path): 分离文件名与扩展名;默认返回(fname,fextension)元组,可做分片操作

os.path.getatime(path): 返回path所指向的文件或者目录的最后存取时间

os.path.getmtime(path): 返回path所指向的文件或者目录的最后修改时间

os.path.join(path1[, path2[, path3..]]):

os.path.join 可以传入多个参数

  • 会从第一个以”/”开头的参数开始拼接,之前的参数全部丢弃;
  • 以上一种情况为先。在上一种情况确保情况下,若出现”./”开头的参数,会从”./”开头的参数的上一个参数开始拼接。
import os

print "1:", os.path.join('aaaa', '/bbbb', 'ccccc.txt')
# 1: /bbbb/ccccc.txt

print "2:", os.path.join('/aaaa', '/bbbb', '/ccccc.txt')
# 2: /ccccc.txt

print "3:", os.path.join('aaaa', './bbb', 'ccccc.txt')
# 3: aaaa/./bbb/ccccc.txt

 os.stat()

方法用于在给定的路径上执行一个系统 stat 的调用

import os
print os.stat("/root/python/zip.py")
# (33188, 2033080, 26626L, 1, 0, 0, 864, 1297653596, 1275528102, 1292892895)
print os.stat("/root/python/zip.py").st_mode   #权限模式
# 33188
print os.stat("/root/python/zip.py").st_ino   #inode number
# 2033080
print os.stat("/root/python/zip.py").st_dev    #device
# 26626
print os.stat("/root/python/zip.py").st_nlink  #number of hard links
# 1
print os.stat("/root/python/zip.py").st_uid    #所有用户的user id
# 0
print os.stat("/root/python/zip.py").st_gid    #所有用户的group id
# 0
print os.stat("/root/python/zip.py").st_size  #文件的大小,以位为单位
# 864
print os.stat("/root/python/zip.py").st_atime  #文件最后访问时间
# 1297653596
print os.stat("/root/python/zip.py").st_mtime  #文件最后修改时间
# 1275528102
print os.stat("/root/python/zip.py").st_ctime  #文件创建时间
# 1292892895

 

socket.getaddrinfo()

函数原型:socket.getaddrinfo(host, port[, family[, socktype[, proto[, flags]]]])

返回值:[(family, socktype, proto, canonname, sockaddr)]有元组组成的列表,元组里面包含5个元素,其中sockaddr是(host, port)

  • family:表示socket使用的协议簇,常用的协议簇包括AF_UNIX(本机通信) 、AF_INET(TCP/IP协议簇中的IPv4协议)、AF_INET6(TCP/IP协议簇中的IPv4协议)。python的socket包中AF_UNIX=1, AF_INET=2, AF_INET6=10
  • sockettype:表示socket的类型,常见的socket类型包括SOCK_STREAM(TCP流)、SOCK_DGRAM(UDP数据报)、SOCK_RAW(原始套接字),SOCK_STREAM=1,  SOCK_DGRAM=2, SOCK_RAW=3
  • proto:套接口所用的协议,若不指定,可用0,常用的协议有IPPROTO_TCP(6)和IPPTOTO_UDP(17),他们分别对应TCP传输协议、UDP传输协议
import socket
print socket.getaddrinfo('www.baidu.com', 80)
# [(2, 2, 17, '', ('180.97.33.108', 80)), (2, 1, 6, '', ('180.97.33.108', 80)), (2, 2, 17, '', ('180.97.33.107', 80)), (2, 1, 6, '', ('180.97.33.107', 80))]

struct模块pack和unpack

pack(fmt, v1, v2….):按照给定格式将数据封装成字符串(实际类似于c结构体的字节流)

unpack(fmt, string ):按照给定格式解析字节流,返回解析出的tuple

calcsize(fmt):计算给定的格式占用多少字节的内存

参考博客: https://blog.csdn.net/w83761456/article/details/21171085

# coding: utf-8
import struct

a = 'hello'
b = 'world!'
c = 2
d = 45.123

bytes = struct.pack('5s6sif', a, b, c, d)
print bytes
# helloworld!�}4B

a, b, c, d = struct.unpack('5s6sif', bytes)
print a, b, c, d
# hello world! 2 45.1230010986

metaclass

允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。多喜欢自定义__new__方法

class Trick(FlyToSky):

  pass

当我们在创建上面的类的时候,python做了如下的操作:

Trick中有__metaclass__这个属性吗?如果有,那么Python会在内存中通过__metaclass__创建一个名字为Trick的类对象,也就是Trick这个东西。如果Python没有找到__metaclass__,它会继续在自己的父类FlyToSky中寻找__metaclass__属性,并且尝试以__metaclass__指定的方法创建一个Trick类对象。如果Python在任何一个父类中都找不到__metaclass__,它也不会就此放弃,而是去模块中搜寻是否有__metaclass__的指定。如果还是找不到,那就是使用默认的type来创建Trick。

那么问题来了,我们要在__metaclass__中放置什么呢?答案是可以创建一个类的东西,type,或者任何用到type或子类化type的东西都行。

参考博客:  https://www.cnblogs.com/piperck/p/5840443.html

class UpperAttrMetaClass(type):
    def __new__(mcs, class_name, class_parents, class_attr):
        attrs = ((name, value) for name, value in class_attr.items() if not name.startswith('__'))
        uppercase_attrs = dict((name.upper(), value) for name, value in attrs)
        return super(UpperAttrMetaClass, mcs).__new__(mcs, class_name, class_parents, uppercase_attrs)


class Trick(object):
    __metaclass__ = UpperAttrMetaClass
    bar = 12
    money = 'unlimited'

print Trick.BAR
print Trick.MONEY

设置守护进程 setDaemon(flag)

当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行流的最小单元,当设置多线程时,主线程会创建多个子线程,在python中,默认情况下(其实就是setDaemon(False)),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束,当我们使用setDaemon(True)方法,设置子线程为守护线程时,主线程一旦执行结束,则全部线程全部被终止执行,可能出现的情况就是,子线程的任务还没有完全执行结束,就被迫停止。

线程join方法

join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程再终止。

join有一个timeout参数:

  • 设置守护线程,主线程对于子线程等待timeout的时间将会杀死该子线程,最后退出程序。所以说,如果有10个子线程,全部的等待时间就是每个timeout的累加和。简单的来说,就是给每个子线程一个timeout的时间,让他去执行,时间一到,不管任务有没有完成,直接杀死。
  • 没有设置守护线程,主线程将会等待timeout的累加和这样的一段时间,时间一到,主线程结束,但是并没有杀死子线程,子线程依然可以继续执行,直到子线程全部结束,程序退出。

参考博客:https://www.cnblogs.com/cnkai/p/7504980.html

 

functools.wraps

在使用装饰器时难免会损失一些东西,使用functools.wraps可以将原函数对象的指定属性复制给包装函数对象, 默认有 __module__、__name__、__doc__,或者通过参数选择。

from functools import wraps

def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)

    return with_logging

@logged
def f(x):
    """does some math"""
    return x + x * x

print f.__name__
# 不加wraps: with_logging
# 加wraps: f
print f.__doc__
# 不加wraps: None
# 加wraps: does some math

 

孤儿进程和僵尸进程

什么是孤儿进程?

一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

什么是僵尸进程?

首先内核会释放终止进程(调用了exit系统调用)所使用的所有存储区,关闭所有打开的文件等,但内核为每一个终止子进程保存了一定量的信息。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。

而僵尸进程就是指:一个进程执行了exit系统调用退出,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态)的进程。

任何一个子进程(init除外)在exit后并非马上就消失,而是留下一个称为僵尸进程的数据结构,等待父进程处理。这是每个子进程都必需经历的阶段。另外子进程退出的时候会向其父进程发送一个SIGCHLD信号。

如何避免僵尸进程?

  • 通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。
  • 父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞。waitpid可以通过传递WNOHANG使父进程不阻塞立即返回。
  • 如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。
  • 通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进程其父进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。

参考博客:https://www.cnblogs.com/Anker/p/3271773.html

selectpollepoll三者的区别

为何使用select 单进程实现并发?

因为一个进程实现多并发比多线程是实现多并发的效率还要高,因为启动多线程会有很多的开销,而且CPU要不断的检查每个线程的状态,确定哪个线程是否可以执行。

那么单进程是如何实现多并发的呢?

之前的socket一个进程只能接收一个连接,当接收新的连接的时候产生阻塞,因为这个socket进程要先和客户端进行通信,二者是彼此互相等待的【客户端发一条消息,服务端收到,客户端等着返回....服务端等着接收.........】一直在阻塞着,这个时候如果再来一个连接,要等之前的那个连接断了,这个才可以连进来。也就是说用基本的socket实现多进程是阻塞的。为了解决这个问题采用每来一个连接产生一个线程,是不阻塞了,但是当线程数量过多的时候,对于cpu来说开销和压力是比较大的。

客户端发起一个连接,会在服务端注册一个文件句柄,服务端会不断轮询这些文件句柄的列表,主进程和客户端建立连接而没有启动线程,这个时候主进程和客户端进行交互,其他的客户端是无法连接主进程的,为了实现主进程既能和已连接的客户端收发消息,又能和新的客户端建立连接,就把轮询变的非常快(死循环)去刷客户端连接进来的文件句柄的列表,只要客户端发消息了,服务端读取了消息之后,有另一个列表去接收给客户端返回的消息,也不断的去刷这个列表,刷出来后返回给客户端,这样和客户端的这次通信就完成了,但是跟客户端的连接还没有断,但是就进入了下一次的轮询。

三者区别

select:单进程实现并发

poll: 它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。

epoll: 直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法, epoll可以同时支持水平触发和边缘触发, epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表 就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了 这些文件描述符在系统调用时复制的开销, 另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描 述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调 机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

水平触发和边缘触发

水平触发:select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll() 的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发。

边缘触发:只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发。

参考博客:https://www.cnblogs.com/qianyuliang/p/6551553.html

atexit模块

atexit只定义了一个register函数用于注册程序退出时的回调函数,我们可以在这个回调函数中做一些资源清理的操作

import atexit

def exit0(*args, **kwarg):
    print 'exit0'
    for arg in args:
        print arg

    for item in kwarg.items():
        print item

def exit1():
    print 'exit1'

atexit.register(exit0, *[1, 2, 3], **{"a": 1, "b": 2, })
atexit.register(exit1)

@atexit.register
def exit2():
    print 'exit2'

# 回调函数执行的顺序与它们被注册的顺序刚才相反
# exit2
# exit1
# exit0
# 1
# 2
# 3
# ('a', 1)
# ('b', 2)

 

shutil模块

高级的 文件、文件夹、压缩包处理模块

常用函数

copyfileobj(文件1,文件2):将文件1的数据覆盖copy给文件2

copyfile(文件1,文件2):不用打开文件,直接用文件名进行覆盖copy

copymode(文件1,文件2):之拷贝权限,内容组,用户,均不变

copystat(文件1,文件):只拷贝了权限

copy(文件1,文件2):拷贝文件和权限都进行copy

copy2(文件1,文件2):拷贝了文件和状态信息

copytree(源目录,目标目录):可以递归copy多个目录到指定目录下

rmtree(目标目录):可以递归删除目录下的目录及文件

move(源文件,指定路径):递归移动一个文件

make_archive():可以压缩,打包文件

psutil模块

psutil是一个跨平台库(http://pythonhosted.org/psutil/)能够轻松实现获取系统运行的进程和系统利用率(包括CPU、内存、磁盘、网络等)信息。它主要用来做系统监控,性能分析,进程管理。它实现了同等命令行工具提供的功能,如ps、top、lsof、netstat、ifconfig、who、df、kill、free、nice、ionice、iostat、iotop、uptime、pidof、tty、taskset、pmap等。目前支持32位和64位的Linux、Windows、OS X、FreeBSD和Sun Solaris等操作系统

参考博客:https://www.cnblogs.com/saneri/p/7528283.html

inspect模块

主要用处

  • 对是否是模块、框架、函数进行类型检查
  • 获取源码
  • 获取类或者函数的参数信息
  • 解析堆栈

常用函数

getargspec(func):返回一个命名元组ArgSpect(args, varargs, keywords, defaults),args是函数位置参数名列表,varargs是*参数名,keywords是**参数名,defaults是默认参数值的元组。

getmembers(object[, predicate]):返回一个包含对象的所有成员的(name, value)列表。返回的内容比对象的__dict__包含的内容多,源码是通过dir()实现的。predicate是一个可选的函数参数,被此函数判断为True的成员才被返回。

getmodule(object):返回定义对象的模块

getsource(object):返回对象的源代码

getsourcelines(object):返回一个元组,元组第一项为对象源代码行的列表,第二项是第一行源代码的行号

stack():第一列是对象名,第二列是当前脚本文件名,第三列是行数,第四列是函数名,第五列是具体执行的程序。第一行是当前函数,第二行是父级函数,以此往上推

threading.local()

这个方法的特点用来保存一个全局变量,但是这个全局变量只有在当前线程才能访问,localVal.val = name这条语句可以储存一个变量到当前线程,如果在另外一个线程里面再次对localVal.val进行赋值,那么会在另外一个线程单独创建内存空间来存储,也就是说在不同的线程里面赋值 不会覆盖之前的值,因为每个线程里面都有一个单独的空间来保存这个数据,而且这个数据是隔离的,其他线程无法访问

import threading
# 创建全局ThreadLocal对象:
localVal = threading.local()
localVal.val = "Main-Thread"


def process_student():
    print '%s (in %s)' % (localVal.val, threading.current_thread().name)


def process_thread(name):
    #赋值
    localVal.val = name
    process_student()
    

t1 = threading.Thread(target=process_thread, args=('One',), name='Thread-A')
t2 = threading.Thread(target=process_thread, args=('Two',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
print localVal.val

# One (in Thread-A)
# Two (in Thread-B)
# Main-Thread

logging模块

logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等;相比print,具备如下优点:

  • 可以通过设置不同的日志等级,在release版本中只输出重要信息,而不必显示大量的调试信息
  • print将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging则可以由开发者决定将信息输出到什么地方,以及怎么输出
# 基本使用
import logging
logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")

参考博客:https://www.cnblogs.com/liujiacai/p/7804848.html

日志高级版zipkin

参考博客:https://blog.csdn.net/liumiaocn/article/details/80657943

 

posted @ 2018-12-02 16:32  wust_ouyangli  阅读(374)  评论(0编辑  收藏  举报