每日编程 2021-03-22

本文介绍了Python中的进程、线程、装饰器和位运算的相关概念与使用。进程包括多进程和进程间通信,如multiprocessing模块的Process和Queue;线程涉及多线程、线程同步与互斥锁,如threading模块的Thread和Lock;装饰器用于函数功能扩展,而位运算符用于二进制数据操作。此外,还讨论了时间日期处理和逻辑运算符的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python

1、断言assert

Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。

断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况。

assert expression

#等价于
if not expression:
    raise AssertionError
触发异常

我们可以使用raise语句自己触发异常

raise语法格式如下:

raise [Exception [, args [, traceback]]]

语句中 Exception 是异常的类型(例如,NameError)参数标准异常中任一种,args 是自已提供的异常参数。

最后一个参数是可选的(在实践中很少使用),如果存在,是跟踪异常对象。

2、装饰器

python装饰器

python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
装饰器的功能特点:
1、不修改已有函数的源代码
2、不修改已有函数的调用方式
3、给已有函数增加额外的功能
4、闭包和装饰器的区分

如果闭包函数的参数有且只有一个,并且是函数类型,那么这个闭包函数称之为装饰器。

之前学过软件设计模式,有一个装饰者模式,这里的装饰器差不多也是实现这个意思。

参考博文

3、进程
multiprocess模块

multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
  multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
  与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。

Process函数

Process([group [, target [, name [, args [, kwargs]]]]])

由该类实例化得到的对象,表示一个子进程中的任务(尚未启动);

在windows中Process()必须放到 if __name__ == '__main__':

由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。
这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句中的语句将不会在导入时被调用。
  1. 需要使用关键字的方式来指定参数
  2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
  3. group参数未使用,值始终为None
  4. target表示调用对象,即子进程要执行的任务
  5. args表示调用对象的位置参数元组,args=(1,2,‘egon’,)
  6. kwargs表示调用对象的字典,kwargs={‘name’:‘egon’,‘age’:18}
  7. name为子进程的名称

p.start():启动进程,并调用该子进程中的p.run()

p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程

from multiprocessing import  Process
import time
if __name__ == '__main__':
    #父进程
    #创建进程,name是你的进程名字
    p1 = Process(target=task1,args=(1,),name='eatella的任务1 ')
    #run只是执行task1,没有启动进程start是开辟进程运行
    p1.start()
    print(p1.name)
    p2 = Process(target=task2,args=(2,),name='eatella的任务2')
    p2.start()
    print(p2.name)
    while True:
        num += 1
        time.sleep(0.1)
        if num == 100:
            p1.terminate()
            p2.terminate()
            break

1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2 p.name:进程的名称
3 p.pid:进程的pid
4 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
5 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功

自定义进程

继承父类Process类,然后需要重写run方法;

进程池

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务;

阻塞式:pool.apply() 这个方法只能将一个任务分配个一个进程,如果想要多任务并行,就需要多次调用 ;

非阻塞式:全部添加到队列中,立刻返回回调,并没有等待所有进程执行完成;pool.apply_async(), apply_async实现了并行执行

multiprocessing.Pool常用函数解析:

  • apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
  • close():关闭Pool,使其不再接受新的任务;
  • terminate():不管任务是否完成,立即终止;
  • join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;
  • get([ 超时] )到达时返回结果。如果超时不是None,并且结果未在超时秒 内到达multiprocessing.TimeoutError则会引发。如果远程调用引发异常,则该异常将被重新引发get()。
  • ready()返回呼叫是否已完成。
  • successful()返回是否完成调用而不引发异常。AssertionError如果结果没有准备就会提高。
from multiprocessing import Pool
import time
from random import random
#非阻塞式进程

def task(task_name):
    print("Estella的任务:",task_name)
    start = time.time()
    time.sleep(random()*2)
    end = time.time()
    print('任务完成时间:',(end-start))
    return task_name

con = []
def callback_func(n):
    con.append(n)

if __name__ == '__main__':
    pool = Pool(5)
    #callback是将task的返回值作为参数传给后面的callback_func函数
    for i in range(8):
        pool.apply_async(task,args=(i,),callback=callback_func)

    pool.close()#停止添加进程
    pool.join()#阻塞主进程,让主进程让步
    print(con)
进程间通信

每个进程都有自己的地址空间、内存、数据栈以及其他记录其运行状态的辅助数据,进程之间没有共享信息。

python中的multiprocessing 模块包装了底层的机制,提供了 Queue(队列)、Pipes(管道)等多种方式来交换数据。

队列Queue的常用方法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTQkpUD2-1616423712800)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\1616407722105.png)]

僵尸进程

一个进程使用了fork创建了子进程,如果子进程终止进入僵死状态,而父进程并没有调用wait或者waitpid获取子进 程的状态信息,那么子进程仍留下一个数据结构保存在系统中,这种进程称为僵尸进程。
僵尸进程会占用一定的内存空间,还占用了进程号,所以一定要避免大量的僵尸进程产生。有很多方法可以避免僵 尸进程。

孤儿进程

父进程退出,而它的子进程仍在运行,那么这些子进程就会成为孤儿进程。孤儿进程会被init进程(进程号为1)收 养,并由init进程对它们完成状态收集工作。
init进程会循环调用wait这些孤儿进程,所以,孤儿进程没有什么危害。

守护进程

它是运行在后台的一种特殊进程。它独立于控制终端并周期性执行某种任务或等待处理某些事件。
守护进程的父进程是init进程,因为其父进程已经故意被终止掉了。
守护进程相对于普通的孤儿进程需要做一些特殊处理

4、多线程

多线程类似于同时执行多个不同程序,多线程运行有如下优点:

  • 使用线程可以把占据长时间的程序中的任务放到后台去处理。
  • 用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
  • 程序的运行速度可能加快。
  • 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。

指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。

  • 线程可以被抢占(中断)。
  • 在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) – 这就是线程的退让。

线程可以分为:

  • **内核线程:**由操作系统内核创建和撤销。
  • **用户线程:**不需要内核支持而在用户程序中实现的线程。

Python3 线程中常用的两个模块为:

  • _thread
  • threading(推荐使用)

thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 “_thread”。

Python中使用线程有两种方式:函数或者用类来包装线程对象。

使用 threading 模块创建线程
threading 模块提供了一个 Thread 类来代表一个线程对象,其语法如下:
Thread(group [, target [, name [, args [, kwaegs]]]])
group:参数未使用,值始终为 None;
target:表示一个可调用对象,线程启动时,run() 方法将调用此对象,默认值为 None,表示不调用任何内容;
name:为当前线程名称,默认创建一个 Thread-N 格式的唯一名称;
args:表示传递给 target 函数的参数元组;
kwargs:表示传递给 target 函数的参数字典;

互斥锁

在一个进程内的所有线程是共享全局变量的,由于线程可以对全局变量随意修改,这就可能造成多线程之间全局变量的混乱。这就需要用到互斥锁(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。

互斥锁为资源引入一个状态:锁定和非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为 “锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成 “非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
在 threading 模块中使用 Lock 类可以方便处理锁定。Lock 类有两个方法:acquire() 锁定和 release() 释放锁。

mutex = threading.Lock()		# 创建锁
mutex.acquire([blocking])		# 锁定
mutex.release()				# 释放锁

mutliprocessing 模块的 Queue 队列可以实现进程间通信,同样在线程间也可以使用 Queue 队列实现线程间通信。不同之处在于我们需要使用 queue 模块的 Queue 队列,而不是 multiprocessing 模块的 Queue 队列,但使用方法相同。

5、位运算

位运算符是将数字看作二进制来进行计算的。

按位与   ( bitwise and of x and y )
&  举例: 5&3 = 1  解释: 101  11 相同位仅为个位1 ,故结果为 1

按位或   ( bitwise or of x and y )
|  举例: 5|3 = 7  解释: 101  11 出现1的位是 1 1 1,故结果为 111

按位异或 ( bitwise exclusive or of x and y )
^  举例: 5^3 = 6  解释: 101  11 对位相加(不进位)是 1 1 0,故结果为 110

按位反转 (the bits of x inverted )
~  举例: ~5 = -6  解释: 将二进制数+1之后乘以-1,即~x = -(x+1),-(101 + 1) = -110
按位反转仅能用在数字前面。所以写成 3+~5 可以得到结果-3,写成3~5就出错了

按位左移 ( x shifted left by n bits )
<< 举例:  5<<2 = 20 解释:101 向左移动2位得到 10100 ,即右面多出2位用0补

按位右移 ( x shifted right by n bits )
>> 举例: 5>>2 = 1  解释:101 向右移动2位得到 1,即去掉右面的2位

6、python的time模块

time模块官方文档

用来处理与时间日期相关的功能;

方法说明
time.time()用来获取时间戳(即:从1970年1月1日00:00:00到现在时间的秒数)
time.localtime()默认获取当前时间的信息,返回格式为元组,返回struct_time。
time.strftime()strftime()可以将localtime()中获取的时间元组转换成自定义的日期时间格式。
time.strptime()strptime( )函数用于根据format的格式把一个时间字符串解析为时间元组。
time.mktime()将元组形式的日期时间转换为时间戳,必须有参数
time.sleep()在给定的秒数内暂停调用线程的执行。该参数可以是浮点数,以指示更精确的睡眠 时间。
time.ctime()将自纪元以来的时间(以秒为单位)转换为以下形式的字符串: 表示本地时间。Mon Mar 22 11:52:01 2021
time.gmtime()自纪元以来的时间(秒)转换为UTC,其中dst标志始终为零。返回struct_time。
time.clock()函数以浮点数计算的秒数返回当前的CPU时间。用来衡量不同程序的耗时。
time.asctime([t])把一个表示时间的元组或者struct_time表示为 ‘Sun Aug 23 14:31:59 2015’ 这种形式。

时间日期格式化符号:

%y 两位数的年份表示(00-99)

%Y 四位数的年份表示(000-9999)

%m 月份(01-12)

%d 月内中的一天(0-31)

%H 24小时制小时数(0-23)

%I 12小时制小时数(01-12)

%M 分钟数(00=59)

%S 秒(00-59)\r\r\n

%a 本地简化星期名称

%A 本地完整星期名称

%b 本地简化的月份名称

%B 本地完整的月份名称

%c 本地相应的日期表示和时间表示

%j 年内的一天(001-366)

%p 本地A.M.或P.M.的等价符

%U 一年中的星期数(00-53)星期天为星期的开始

%w 星期(0-6),星期天为星期的开始

%W 一年中的星期数(00-53)星期一为星期的开始

%x 本地相应的日期表示

%X 本地相应的时间表示

%Z 当前时区的名称
import time

print(time.time())
print(time.ctime())
print(time.localtime())
print(time.localtime(time.time()))#时间戳转化为元组
print(time.mktime(time.localtime()))#元组转化为时间戳
print(time.gmtime(134654545))#时间戳转化为元组
print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime()))
print(time.strptime(time.strftime("%a %b %d %H:%M:%S %Y",time.localtime())))

'''
1616386927.7850363
Mon Mar 22 12:22:07 2021
time.struct_time(tm_year=2021, tm_mon=3, tm_mday=22, tm_hour=12, tm_min=22, tm_sec=7, tm_wday=0, tm_yday=81, tm_isdst=0)
time.struct_time(tm_year=2021, tm_mon=3, tm_mday=22, tm_hour=12, tm_min=22, tm_sec=7, tm_wday=0, tm_yday=81, tm_isdst=0)
1616386927.0
time.struct_time(tm_year=1974, tm_mon=4, tm_mday=8, tm_hour=12, tm_min=2, tm_sec=25, tm_wday=0, tm_yday=98, tm_isdst=0)
2021-03-22 12:22:07
time.struct_time(tm_year=2021, tm_mon=3, tm_mday=22, tm_hour=12, tm_min=22, tm_sec=7, tm_wday=0, tm_yday=81, tm_isdst=-1)

'''
7、python的逻辑运算符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VTyzTCLN-1616423855435)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\1616389458812.png)]

java

1、位运算

Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节类型(byte)等类型。

Java包含了七种位运算符

位运算符说明
>>右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,若为正数则高位补0,若为负数则高位补1
<<左移运算符,符号左侧数值 按位左移 符号右侧数值指定的位数,并在低位处补0
>>>无符号右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,无论正负高位补0
&(AND)运算符,对两个整型操作数中对应位执行布尔代数,两个位都为1时输出1,否则0
|(OR)运算符,对两个整型操作数中对应位执行布尔代数,两个位中只要有一个为1就输出1,否则为0
^异或(XOR)运算符,对两个整型操作数中对应位执行布尔代数,两个位相等则为0,不相等则为1
~(NOT)运算符,按位取反运算符翻转操作数的每一位,即0变成1,1变成0

应用参考:https://blog.youkuaiyun.com/weixin_37490221/article/details/90905087

2、java的堆内存和栈内存
java堆内存

堆内存在Java运行时被使用来为对象和JRE类分配内存。不论什么时候我们创建了对象,它将一直会在堆内存上创建。垃圾回收运行在堆内存上来释放没有任何引用的对象所占的内存,任何在堆上被创建的对象都有一个全局的访问,并且可以在应用的任何位置被引用。

堆内存是java内存中的一种,它的作用是用于存储java中的对象和数组,当我们new一个对象或者创建一个数组的时候,就会在堆内存中开辟一段空间给它,用于存放。由java虚拟机的自动垃圾回收器来管理。

第一点:堆其实可以类似的看做是管道,或者说是平时去排队买票的情况差不多,所以堆内存的特点就是:先进先出,后进后出,也就是你先排队好,你先买票。
第二点:堆可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,但缺点是,由于要在运行时动态分配内存,存取速度较慢。

java栈内存

栈内存是Java的另一种内存,主要是用来执行程序用的,比如:基本类型的变量和对象的引用变量
栈内存的特点
第一点:栈内存就好像一个矿泉水瓶,往里面放入东西,那马先放入的沉入底部,所以它的特点是:先进后出,后进先出
第二点:存取速度比堆要快,仅次于寄存器,栈数据可以共享,但缺点是,存在栈中的数据大小与生存必须是确定的,缺乏灵活性
栈内存可以称为一级缓存,由垃圾回收器自动回收
JVM是基于堆栈的虚拟机,JVM为新创建的线程都分配一个堆栈,也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

堆栈的异同

差异:
1). 堆内存用来存放由new创建的对象和数组
2). 栈内存用来存放方法或者局部变量等
3). 堆是先进先出,后进后出
4). 栈是先进后出,后进先出
5). 共享性的不同:

栈内存是线程私有的
堆内存是所有线程共有的

6). 异常错误不同:

如果栈内存或者堆内存不足都会抛出异常。
栈空间不足:java.lang.StackOverFlowError。
堆空间不足:java.lang.OutOfMemoryError。

7). 空间大小
栈的空间大小远远小于堆的。

相同
1). 都是属于Java内存的一种
2). 系统都自动去回收它,但是对于堆内存一般开发人员会自动回收它

3、其他/hjxbrlbt

Java中的基本类型功能简单,不具备对象的特性,为了使基本类型具备对象的特性,所以出现了包装类,就可以像操作对象一样操作基本类型数据。

Java有8种基本数据类型:整型(byte、short、int、long)、浮点型(float、double)、布尔型boolean、字符型char,相对应地,Java提供了8种包装类Byte、Short、Integer、Long、Float、Double、Boolean、Character。包装类创建对象的方式就跟其他类一样。

数据类型转换必须满足如下规则:

  • \1. 不能对boolean类型进行类型转换。
  • \2. 不能把对象类型转换成不相关类的对象。
  • \3. 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
  • \4. 转换过程中可能导致溢出或损失精度

每日编程

191-位1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。

汉明重量是一串符号中非零符号的个数。因此它等同于同样长度的全零符号串的汉明距离。在最为常见的数据位符号串中,它是1的个数。

提示:

请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。

示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。

示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

提示:

输入必须是长度为 32 的 二进制串 。

class Solution:
    def hammingWeight(self, n: int) -> int:
        ret = sum(1 for i in range(32) if n & (1 << i)) //与运算看有多少位1
        return ret

制串 00000000000000000000000000001011 中,共有三位为 ‘1’。


示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。


示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。



提示:

输入必须是长度为 32 的 二进制串 。

```python
class Solution:
    def hammingWeight(self, n: int) -> int:
        ret = sum(1 for i in range(32) if n & (1 << i)) //与运算看有多少位1
        return ret
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值