学习笔记-Python开发-018_函数入门

1 初识函数

1.1 函数是什么?

我们可以把函数直接看成一大堆功能代码的集合

# 函数的定义方式
def 函数名():
    '''函数的注释
    函数内代码
    :return: 返回值
    '''
    pass

# 函数的调用方式
函数名()

例如:

def daydream():
    print("kinght超级帅")
    print("kinght超级瘦")
    print("kinght超有钱")

# 调用函数才会被执行函数里面的代码
daydream()
daydream()

1.2 什么时候使用函数

什么时候会用到函数呢?一般在项目开发中有会有两种应用场景:

1.2.1 有重复代码,使用函数增加代码的重复使用

案例:监控cpu的使用率,并发送邮件

def send_email():
  '''
  发送邮件的代码
  并且,如果发邮件的代码需要修改,也只需要改这里一处,方便维护
  '''
  pass

def cpu_utilization():
  '''
  监控cpu使用率的代码
  '''
  pass

print("欢迎使用计算机监控系统")
cpu_utilization()
if CPU占用率 > 90%:
  # 这里会重复使用发邮件的代码,使用函数可以直接封装变成一行代码
    send_email()

if 硬盘使用率 > 99%:
    send_email()
    
if 内存使用率 > 98%:
    send_email()
...

1.2.2 代码太长增强代码的可读性

案例:

def calculate_same_num_rule():
    """判断是否是豹子"""
    pass

def calculate_same_color_rule():
    """判断是否是同花"""
    pass

def calculate_straight_rule():
    """判断是否顺子"""
	pass

def calculate_double_card_rule(poke_list):
    """判断是否对子"""
	pass

def calculate_single_card_rule():
    """判断是否单牌"""
    pass

# 1. 生成一副扑克牌
card_color_list = ["红桃", "黑桃", "方片", "梅花"]
card_nums = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]  # A
all_card_list = [[color, num] for color in card_color_list for num in card_nums]

# 2.洗牌
random.shuffle(all_card_list)

# 3.给玩家发牌
...
# 4.判断牌是:豹子?同花顺?顺子?对子?单点?
calculate_same_num_rule()
calculate_same_color_rule()
calculate_straight_rule()
...

以前我们变成是按照业务逻辑从上到下逐步完成,称为:面向过程编程;现在学了函数之后,利用函数编程称为:函数式编程

2 函数的参数

2.1 使用python发送邮件

在具体进入函数的参数之前,我们先学一下如何使用python进行邮件的发送

  • 获得一个拥有smtp功能的邮箱
    • 基本所有的邮箱都有,可以在设置中打开smtp
    • 得到smtp服务器的地址
      • 腾讯企业邮箱
        • smtp.exmail.qq.com(使用SSL,端口号465)
  • 发送邮件的代码
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr

def send_mali():
    # 邮件文本内容编辑
    msg = MIMEText("测试",'html','utf-8')
    # 发件人
    msg['From'] = formataddr(['kinght测试python','kinght@geekxk.com'])
    # 邮件标题
    msg['Subject'] = '内容发送测试'

    # 发送邮件设置
    server = smtplib.SMTP_SSL('smtp.exmail.qq.com')
    server.login("kinght@geekxk.com","邮箱密码") # 个别邮箱需要使用专门的授权码
    server.sendmail("kinght@geekxk.com",'419353653@qq.com',msg.as_string())
    server.quit()

send_mali()

2.2 函数参数的应用

写这篇笔记的时候,正值大年三十,我们需要给朋友的邮箱广发新年祝福邮件,很明显,这时候将sendmail里面的值都被写死了,这时候就需要进行一定的改造

import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr

user_qq_list = ['QQ号码1','QQ号码2','QQ号码3','QQ号码4','QQ号码5']

def send_mali(user_qq):
    text = r'''
    新年快乐,万事如意
    '''
    # 邮件文本内容编辑
    msg = MIMEText(text,'html','utf-8')
    # 发件人
    msg['From'] = formataddr(['kinght测试python','kinght@geekxk.com'])
    # 邮件标题
    msg['Subject'] = '新年快乐'

    # 发送邮件设置
    server = smtplib.SMTP_SSL('smtp.exmail.qq.com')
    server.login("kinght@geekxk.com","邮箱密码")
    ## user_qq变量值来源于循环中的qq_number
 		server.sendmail("kinght@geekxk.com",'{}@qq.com'.format(user_qq),msg.as_string())
    server.quit()

for qq_number in user_qq_list:
    send_mali(qq_number)

循环调用发送邮件的函数,将user_qq_list列表进行循环取出QQ号码,然后调用函数send_mali,在send_mali(qq_number)括号里面的值,会被传送到def send_mali(user_qq)括号里,相当于qq_number => user_qq,然后user_qq可以在send_mali函数中直接作为变量使用

2.3 形参与实参

在定义函数时,如果在括号中添加变量,我们称它为函数的形式参数:

Tips(个人注解):

形式参数的含义指的是在函数中占位,在函数编程中能够占位,以便实际参数传递过来后能够有位置进行处理的参数

# 定义有三个参数的函数(a1/a2/a3一般称为形式参数-形参)
def func(a1,a2,a3):
    print(a1+a2+a3)

# 执行函数并传入参数(执行函数传值时一般称为实际参数-实参)
## 有几个形参,在调用时就要有几个实参
## 执行函数并传入参数
func(11,22,33)

# 参数传递的两种方式
## 按顺序传参
def demo(a,b,c):
    print(a,b,c)
print(1,2,3)
## 按关键字传参
demo(b=1,a=2,c=3)
## 顺序和关键值可以混合,但关键字必须在顺序后面,且形参和实参数量对等
demo(3,c=2,b=4)

2.4 默认参数

形参与实参的数量要对齐,但是我们也遇到过使用函数的时候,同一函数传入两个或者三个参数的情况,而使用的时候效果并没有差异

file_object = open("user.text",encoding='utf-8') # 默认 mode = 'rt'
file_object = open("user.text",mode='rt',encoding='utf-8')

其实在定义的时候就可以提前给形参赋值,就可以达到有实参传入使用实参,没有实参传入就使用提前赋的值(默认参数)

def add(a,b,c,d=10):
    print(a+b+c+d)
add(1,2,3) # 1+2+3+10=16
add(1,2,3,4) # 1+2+3+4=10
add(1,2,d=3,c=4) # 1+2+4+3=10

2.5 动态参数

现在需要编写一个加法计算器进行运算,但是我并不知道有多少个数字,在默认参数中那个add的案例就显得有点捉襟见肘,这时候我们可以使用动态参数解决这个问题

Tips:

在下文中会大量见到args、kwargs这个形参,但其实,args和kwargs并不是python语法规定的动态参数固定名字,只是程序员前辈们为了方便代码阅读而形成的编码潜规则

2.5.1 只能按照位置传参

按照位置传参的特点就是*args,它的实现逻辑是元组args = (),将接收到的值,都放入元组中

def add(*args):
  	print(args) # (1, 2, 3, 4, 5, 6, 7)
    number = 0
    for i in args:
        number += i
    print(number)
add(1,2,3,4,5,6,7)

2.5.2 只能按关键值传参

按关键值传参的特点就是**kwargs,它的实现逻辑是字典kwargs = {},将接收到的值,都成键值对放入字典中

def user(**kwargs):
    print(kwargs)
user(name = "kinght",age = "18",work = "废物")
# 输出 {'name': 'kinght', 'age': '18', 'work': '废物'}

2.5.3 既能通过关键字传参也能按照位置传参

这其实就是上面两种对融合体,在传参后会分别放在对应的元组和字典中,不过需要注意的事情就是按照位置传参无论是定义还是传参,都必须在按关键值传参之前

def demo(*args,**kwargs):
    print(args) # (1, 2, 3, 4, 5)
    print(kwargs) # {'username': 'kinght', 'passwd': 'demo'}
# 同样的在调用的时候不允许出现关键字传参在位置传参之前
demo(1,2,3,4,5,username="kinght",passwd="demo")
# 如果只有关键字传参或只有位置传参
demo(1,2,3,4,5) # args = (1, 2, 3, 4, 5) kwargs = {}
demo(a=1,b=2) # args = () kwargs = {'a': 1, 'b': 2}

案例:是否还记得字符串格式化时的format功能

v1 = "我叫{},今年{},性别{}".format("kinght",18,"男")
v2 = "我叫{name},今年{age},性别{gender}".format(name="kinght",age=18,gender="男")

2.5.4 参数的定义和使用顺序

定义顺序和使用顺序是相同的,默认按照

普通参数 -> 动态位置传参 -> 默认参数 -> 动态关键字传参

案例

def demo2(a1, a2, a3, a4=10, *args, a5=20, **kwargs):
    print(a1, a2, a3, a4, a5, args, kwargs)
demo2(11, 22, 33, 44, 55, 66, 77, a5=10, a10=123)
# 输出结果 11 22 33 44 10 (55, 66, 77) {'a10': 123}
11, 22, 33, 44 分别赋值给了 a1, a2, a3, a4
55, 66, 77 赋值给了 args 形成 (55, 66, 77)
a5=10 把10赋值给了 a5
a10=123 把a10=123赋值给了 **kwargs 形成 {'a10': 123}

3 函数的返回值

在开发过程中,我们希望函数可以帮助我们实现某个功能,但让函数实现某功能之后有时也需要有一些结果需要反馈给我们,例如:

def city_weather(city):
    '''
    查询输入城市的天气情况
    :param city: 城市
    :return: weather -> 天气
    '''
    data_list = []
    url = "http://ws.webxml.com.cn//WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName={}".format(city)
    res = requests.get(url=url)
    root = ET.XML(res.text)
    for node in root:
        data_list.append(node.text)
    return data_list

# 可以直接接受return的返回值,然后进行下一步处理
result = city_weather("成都")
print(result)

3.1 返回值的知识点

3.1.1 返回值可以是任意类型

def func():
    return [1,True,(11,22,33)]

result = func()
print(result)

当在函数中如果函数中未写返回值returnreturn None ,执行函数获取的返回值都是None

# 没写返回值
def func():
    value = 1 + 1
ret = func()
print(ret) # None

# 返回值只写了return
def func():
    value = 1 + 1
    return
ret = func()
print(ret) # None

# 返回值写了none
def func():
    value = 1 + 1
    return None
ret = func()
print(ret) # None

3.1.2 return后面的值如果有逗号,则默认会将返回值转换成元组再返回

def func():
    return 1,2,3

value = func()
print(type(value)) # 查看元素类型 <class 'tuple'>
print(value) # (1,2,3)

补充:type(变量名) 等于查看元素类型

3.1.3 函数一旦遇到return就会立即退出函数(终止函数中的所有代码)

def return_demo():
    print("1")
    return "2"
    print("3")

print(return_demo()) # 输出 1 2

3.2 return的用途

3.2.1 完成某个结果并希望的到结果

def add(*args):
    a = 0
    for i in args:
        a += i
    return a
print(add(1,2,3,4,5,6))

3.2.2 基于return控制让函数终止执行

def judge():
    i = '123'
    print(1)
    if i.isdigit(): # 判断i是否是十进制数
        return
    print(2)
judge() # 1

4 案例 -> 账号密码登陆 使用excel做数据库

'''
写函数,读取的用户信息并构造为字典(用户信息存放在`files/user.xlsx`文件中)
# 构造的字典格式如下
user_dict = {
    "用户名":"密码"
    ...
}

用户输入用户名和密码,进行校验。(且密码都是密文,所以,需要将用户输入的密码进行加密,然后再与Excel中的密文密码进行比较)

'''

import hashlib
import os.path

from openpyxl import workbook,load_workbook
from openpyxl.styles import Alignment,PatternFill,Font,Side,Border

def table_style():
    '''表格风格'''
    side = Side(style='thin', color='000000') # 表格线
    border = Border(top=side, bottom=side, left=side, right=side) # 上下左右都运用表格线
    align = Alignment(horizontal='center', vertical='center') # 居中
    return border,align


def file_path():
    '''创建路径'''
    abs_path = os.path.abspath(__file__)
    dir_path = os.path.dirname(abs_path)
    excel_path = os.path.join(dir_path,'file','user.xlsx')
    return excel_path


def dir_excel():
    '''获取表格句柄'''
    excel_path = file_path()
    if not os.path.exists(excel_path):
        wb = workbook.Workbook()
        sheet = wb.worksheets[0]
        # 表头列表
        meter_header_words = {'A1':'ID','B1':'用户名','C1':'密码'}
        for key,text in meter_header_words.items():
            # 设置值
            cell = sheet[key]
            cell.value = text
            # 设置居中
            cell.alignment = table_style()[1]
            # 设置背景颜色
            cell.fill = PatternFill("solid",fgColor='4e72b8')
            # 设置字体
            cell.font = Font(name = "微软雅黑",color="130c0e")
        wb.save(excel_path)
    else:
        wb = load_workbook(excel_path)
    return wb

def import_user(username,password):
    '''注册用户'''
    wb = dir_excel()
    sheet = wb.worksheets[0]
    c1 = sheet['A']
    # 读取空白文档
    for cell in c1:
        if cell.value == 'ID':
            id = 1
        else:
            id = cell.value + 1
    lie_id = id + 1 # 第几列
    # password加密
    password = hashlib.md5(password.encode('utf-8')).hexdigest()
    # 写入表格数据
    cell_id = sheet.cell(lie_id,1)
    cell_id.value = id
    cell_username = sheet.cell(lie_id,2)
    cell_username.value = username
    cell_passwd = sheet.cell(lie_id,3)
    cell_passwd.value = password
    wb.save(file_path())

def login(username,password):
    wb = dir_excel()
    sheet = wb.worksheets[0]
    user_dict = {}
    for row in sheet.rows:
        user_dict[row[1].value] = row[2].value
    # 加密输入的密码
    password = hashlib.md5(password.encode('utf-8')).hexdigest()
    # 然后进行比较
    if username in user_dict:
        if password == user_dict.get(username):
            print("登录成功")
        else:
            print("密码错误")
    else:
        print("账户不存在")


# import_user('root','root')
login('root','roota')
posted @ 2023-02-26 22:35  kinghtxg  阅读(10)  评论(0编辑  收藏  举报