闭包

简略认识一下闭包

以下代码,内层inner函数不只依赖于本身的参数b,还依赖于外层outer函数的参数a。inner便是一个闭包函数,既能拜访外部变量,又保证外部变量不是全局的,不会被篡改掉,保证了外部变量的安全。

def outer(a):
    def inner(b):
        print(f"<{a}>{b}<{a}>")
    return inner
n1 = outer('程序员')  # n1的类型是一个函数
n1('学习python')
n1('学习java')
n2 = outer('软件测验工程师')
n2('功用测验')
n2('自动化测验')

python高阶技巧

假如要在内层函数修正外层函数的变量,需求用nonlocal润饰,示例代码如下:

def outer(num1):
    def inner(num2):
        # 要对外层num1进行修正的话,需求nonlocal润饰
        nonlocal num1
        num1 += num2
        print(num1)
    return inner
fn = outer(10)
fn(5)
# 输出为15

案例:运用闭包办法简略完成ATM存取款

def account_create(initial_amount=0):
    def atm(num, deposit=True):
        nonlocal initial_amount
        if deposit:
            initial_amount += num
            print(f"存款:+{num},账户余额:{initial_amount}")
        else:
            if initial_amount<num:
                print(f"钱不行{num}了,取不出来!")
            else:
                initial_amount -= num
                print(f"取款:-{num},账户余额:{initial_amount}")
    return atm
atm = account_create()
atm(100, deposit=True)
atm(500, deposit=True)
atm(200, deposit=False)
atm(1000, deposit=False)

python高阶技巧

闭包的优缺点:

优点:

无需界说全局变量即可完成经过函数,持续的拜访、修正某个值

闭包运用的变量位于在函数内,难以被错误的调用修正

缺点:

因为内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一向占用内存

装修器

装修器其实也是一种闭包,其功用便是在不损坏方针函数原有的代码和功用的前提下,为方针函数添加新功用。

示例代码:

def outer(func):
    def inner():
        print("我要睡觉了......")
        func()
        print("睡醒了,我要起床了......")
    return inner()
@outer # 相当于给sleep添加outer的装修器
def sleep():
    import random
    import time
    print("睡眠中......")
    time.sleep(random.randint(1, 5))
sleep()

python高阶技巧

单例办法

背景

class Tool:
    pass
t1 = Tool()
t2 = Tool()
print(t1)
print(t2)
# 输出成果:
# <__main__.Tool object at 0x000001CEB2B190D0>
# <__main__.Tool object at 0x000001CEB2B190A0>

经过print句子能够看出,它们的内存地址是不相同的,即t1和t2是完全独立的两个目标。

某些场景下,咱们需求一个类无论获取多少次类目标,都仅仅提供一个具体的实例,用以节约创立类目标的开支和内存开支,比如某些工具类,仅需1个实例,即可在遍地运用。

这便是单例办法所要完成的作用。

界说:

保证一个类只有一个实例,并提供一个拜访它的全局拜访点

适用场景:当一个类只能有一个实例,而客户能够从一个众所周知的拜访点拜访它

单例的完成办法:

python高阶技巧

工厂办法

背景:

当需求大量创立一个类的实例的时分,能够运用工厂办法即,从原生的运用类的结构去创立目标的办法迁移到,根据工厂提供的办法去创立目标的办法

class Person:
    pass
class Worker(Person):
    pass
class Student(Person):
    pass
class Teacher(Person):
    pass
worker = Worker()
stu = Student()
teacher = Teacher()

以上是传统办法构建根据Person的不同类目标。

采用工厂办法,代码就会变成如下:

class Person:
    pass
class Worker(Person):
    pass
class Student(Person):
    pass
class Teacher(Person):
    pass
class Factory:
    def get_person(self, p_type):
        if p_type == 'w':
            return Worker()
        elif p_type == 's':
            return Student()
        else:
            return Teacher()
factory = Factory()
worker = factory.get_person('w')
stu = factory.get_person('s')
teacher = factory.get_person('t')

运用工厂类的get_person()办法去创立具体的类目标优点:

大批量创立目标的时分有一致的入口,易于代码保护;

当发生修正,仅修正工厂类的创立办法即可;

契合现实国际的办法,即由工厂来制作产品(目标)

线程编程(threading)

进程:便是一个程序,运转在体系之上,那么便称之这个程序为一个运转进程,并分配进程ID方便体系管理。

线程:线程是归属于进程的,一个进程能够敞开多个线程,履行不同的作业,是进程的实际作业最小单位

并行履行

多个进程一起在运转,即不同的程序一起运转,称之为: 多使命并行履行;

一个进程内的多个线程一起在运转,称之为:多线程并行履行;

写一段代码,咱们先看下单线程运转下成果:

import time
def sing():
    while True:
        print("我在歌唱,啦啦啦......")
        time.sleep(1)
def dance():
    while True:
        print("我在跳舞,呱呱呱......")
        time.sleep(2)
if __name__ == '__main__':
    sing()
    dance()

python高阶技巧

要想输出既在歌唱,又在跳舞,是无法满意的。

运用多线程的话,一个线程在歌唱,一个线程在跳舞,就能够满意需求。

语法:

import threading

thread_obj = threading.Thread([group [,target [,name [, args [,kwargs]]]]])

  • group: 暂时无用,未来功用的预留参数
  • target: 履行的方针使命名
  • args: 以元组的办法给履行使命传参
  • kwargs:以字典办法给履行使命传参
  • name: 线程名,一般不用设置

启动线程,让线程开端作业thread_obi.start()

import time
import threading
def sing():
    while True:
        print("我在歌唱,啦啦啦......")
        time.sleep(1)
def dance():
    while True:
        print("我在跳舞,呱呱呱......")
        time.sleep(2)
if __name__ == '__main__':
    # 创立一个歌唱的线程
    sing_thread = threading.Thread(target=sing)
    # 创立一个跳舞的线程
    dance_thread = threading.Thread(target=dance)
    # 运转线程
    sing_thread.start()
    dance_thread.start()

python高阶技巧

Socket服务端编程

主要分为如下几个过程

1.创立socket目标

import socket

socket_server = socket.socket(0)

2.绑定socket_server到指定IP和地址

socket_server.bind(host, port)

3.服务端开端监听端口

socket_server.listen(backlog)

backlog为int整数,标明答应的衔接数量,超出的会等候,能够不填,不填会自动设置一个合理值

4.接纳客户端衔接获得衔接目标

conn.address = socket_server.accept()

print(f"接纳到客户端衔接,衔接来自: {address}")

accept办法是堵塞办法,假如没有衔接,会卡在当前这一行不向下履行代码

accept回来的是一个二元元组,能够运用上述办法,用两个变量接纳二元元组的2个元素

5.客户端衔接后,经过recv办法,接纳客户端发送的音讯

while True:
    data = conn.recv(1024).decode("UTF-8")
    # recv办法的回来值是字节数组(Bytes》,能够经过decode运用UTF-8解码为字符串
    # recv办法的传参是buffsize,缓冲区巨细,一般设置为1024即可
    if data == 'exit':
        break
    print("接纳到发送来的数据:",data)
# 能够经过while True无限循环来持续和客户端进行数据交互
# 能够经过判定客户端发来的特别标记,如exit,来退出无限循环

6.经过conn(客户端当次衔接目标)调用send办法能够回复音讯

while True:
    data = conn.recv(1024).decode("UTF-8")
    if data == 'exit':
        break
    print("接纳到发送来的数据:", data)
    conn.send("你好呀哈哈哈".encode("UTF-8”))

7.conn(客户端当次衔接目标)和socket_server目标调用close办法,封闭衔接

Socket客户端编程

主要分为如下几个过程:

1.创立socket目标

import socket

socket_client = socket.socket()

2.衔接到服务端

socket_client.connect(("localhost",8888))

3.发送音讯

while True: # 能够经过无限循环来保证持续的发送音讯给服务端
    send_msg = input("请输入要发送的音讯")
    if send_msg == exit':
        # 经过特别标记来保证能够退出无限循环
        break
    socket_client.send(send_msg.encode("UTF-8"))  # 音讯需求编码为字节数组(UTF-8编码)

4.接纳回来音讯

while True:
    send_msg = input("请输入要发送的音讯").encode("UTF-8")
    socket_client.send(send_msg)
    recv_data = socket_client.recv(124) # 1024是缓冲区巨细,一般1024即可
    #recv办法是堵塞式的,即不接纳到回来,就卡在这儿等候
    print("服务端回复音讯为:",recv_data.decode("UTF-8"))#接受的音讯需求经过UTF-8解码为字符串

5.封闭链接

socket_client.close()

正则表达式

正则表达式,又称规矩表达式(Regular Expression),是运用单个字符串来描绘、匹配某个句法规矩的字符串,常被用来检索、替换那些契合某个办法(规矩)的文本。

简略来说,正则表达式便是运用: 字符串界说规矩,并经过规矩去验证字符串是否匹配。

三个根底办法:

运用re模块,并根据re模块(import re)中三个根底办法来做正则匹配。

(1)match

re.match(匹配规矩,被匹配字符串)

从被匹配字符串最初进行匹配,匹配成功回来匹配目标(包含匹配的信息),匹配不成功回来空。

(2)search

re.search(匹配规矩,被匹配字符串)

搜索整个字符串,找出匹配的。早年向后,找到第一个后,就中止,不会持续向后

(3)findall

re.findall(匹配规矩,被匹配字符串)

匹配整个字符串,找出全部匹配项,找不到回来空list:[]

元字符匹配:

(1)单字符匹配:

python高阶技巧

实例:

import re
s = "learn @@python3 12EEAA!!66 ##study3"
# 找出全部数字
result1 = re.findall(r'\d', s)
print(result1)  # ['3', '1', '2', '6', '6', '3']
# 找出特别字符
result2 = re.findall(r'\W', s)
print(result2)  # [' ', '@', '@', ' ', '!', '!', ' ', '#', '#']
# 找出全部英文字母
result3 = re.findall(r'[a-zA-Z]', s)
print(result3)  # ['l', 'e', 'a', 'r', 'n', 'p', 'y', 't', 'h', 'o', 'n', 'E', 'E', 'A', 'A', 's', 't', 'u', 'd', 'y']

(2)数量匹配:

python高阶技巧

(3)鸿沟匹配:

python高阶技巧

(4)分组匹配

python高阶技巧

实例:

# 匹配账号,只能由字母和数字组成,长度限制6到10位
r1 = '^[0-9A-Za-z]{6,10}$'
s1 = '12a3C6'
print(re.findall(r1, s1))  # 输出['12a3C6'],证明匹配成功
# 匹配QQ号,要求纯数字,长度5-11,第一位不为0
r2 = '^[1-9][0-9]{4,10}$'
s2 = '123456987'
print(re.findall(r2, s2))  # 输出['123456987'],证明匹配成功
# 匹配邮箱地址,只答应qq、163、gmail这三种邮箱地址
r3 = r'(^[\w-]+(.[\w-]+)*@(qq|163|gmail)(.[\w-]+)+$)'
s3 = '907218846@qq.com'
print(re.match(r3, s3))

递归

概念:办法(函数)自己调用自己的一种特别编程写法

案例:找出一个文件夹中全部的文件

import os
def test_os():
    # OS模块中的根底办法
    # 将文件夹里面的内容显示出来
    print(os.listdir("E:/python"))
    # 判别给的路径是不是个文件夹
    print(os.path.isdir("E:/python/libs"))
    # 判别指定路径是否存在
    print(os.path.exists("E:/python"))
def get_file(path):
    file_list = []
    if os.path.exists(path):
        for f in os.listdir(path):
            new_path = path + "/" + f
            if os.path.isdir(new_path):
                # 标明目录是文件夹不是文件,运用递归了
                get_file(new_path)
            else:
                file_list.append(new_path)
    else:
        print(f"指定的目录{path}不存在")
        return []
    return file_list
if __name__ == '__main__':
    print(get_file("E:/python"))

python高阶技巧