函数

  • 函数是完成特定功能的一个语句组,这组语句可以作为一个单位使用,并且给它取一个名字,之后即可通过函数名执行。
  • 使用def定义函数,函数名后的圆括号用来放置传递给函数的参数。
  • 有返回值的函数,用return进行返回最终值。函数遇到return语句就会终止当前函数的执行,return之后的语句都会被忽略。
  • 在python中,函数的参数可以有默认值,也支持使用可变参数,因而不需要支持函数重载,而是在定义函数时让它有多种不同的适用方式。
  • 函数重载是一种特殊情况,有些语言允许在同一作用域中声明几个类似的同名函数,这些同名函数的形参列表(参数个数,类型,顺序)必须不同,常用来处理实现功能类似数据类型不同的问题,如不同数据类型的数值交换。
  • python可以给函数参数设定默认值(缺省参数),意味着在没有对应位置参数传入时,将使用默认值,即可以通过不同的方式调用函数,这与函数重载效果一致。
  • 在不确定参数个数时,可以使用可变参数,即在括号中参数名的前面添加*,表示它是一个可变参数。
  • 局部变量只能在特定部分使用,全局变量整个程序都能使用。如果局部变量与全局变量名称相同,在局部变量作用的范围内是局部变量在起作用。如果想在函数内部对全局变量进行修改,需要使用global关键字。
  • 函数优点:代码可重用,提高开发效率;代码更简洁,可读性好;编程更容易把握;封装和信息隐藏。使用函数进行程序设计,被称为结构化程序设计方法。
  • 当两个不同.py文件(即模块,每个.py文件都是一个模块)中含有同样的函数名时,为防止函数覆盖,如module1.py和module.py两个模块都包含foo函数,有以下解决方式。除此之外,其他错误的导入调用方式,都会造成函数覆盖,后导入的会覆盖先导入的。
  • a.调用谁导入谁
from module1 import foo
foo()
from module2 import foo
foo()
  • b.将导入模块重新命名
import module1 as m1
import module2 as m2
m1.foo()
m2.foo()
  • 如果导入的模块除了定义函数还含有可执行代码,那在导入模块的过程中会执行这些代码。因而,如果在模块中编写了执行代码,应当将代码放入if _name_ == '_main_':的条件中,_name_是python中一个隐含的变量,代表了模块名,而只有被解释器直接执行的模块名才是_main_
  • 在这种情况下,该模块被别的模块导入,由于导入名使用的是该模块名而非_main_,在if条件下的代码就不会被执行。
  • python中可以在函数内部再定义函数,可以使用global关键字在函数内定义或使用全局变量,nonlocal关键字可以在函数内使用外层变量。
  • 在实际开发中,应减少对全局变量的定义。全局变量作用于和影响过于广泛,可能会发生意料之外的修改和使用,此外比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被回收,同时也可以降低代码耦合度。
  • 今后,就可以对代码的书写进行改进,书写成一下形式:
def main():
    pass
if __name__ == '__main__':
    main()
  • 函数练习:给定年和月份,获得当月第一天是周几(从1800年1月1号开始计算,当天是周三),代码如下:
def is_leap_year(year):
    if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
        return True
    else:
        return False

def get_num_of_days_in_month(year, month):
    if month in (1, 3, 5, 7, 8, 10, 12):
        return 31
    elif month in (4, 6, 9, 11):
        return 30
    elif is_leap_year(year):
        return 29
    else:
        return 28

def get_total_num_of_day(year, month):
    days = 0
    for y in range(1800, year):
        if is_leap_year(y):
            days += 366
        else:
            days += 365
    for m in range(1, month):
        days += get_num_of_days_in_month(year, m)

    return days

def get_start_day(year, month):
    return (3 + get_total_num_of_day(year, month)) % 7

print(get_start_day(2033, 12))

递归函数

  • 递归表现形式:在函数定义时有直接或者间接调用自身。
  • 递归解决问题的基本思想:if问题足够简单则直接求解,else将问题分解成与原问题同构的一个或多个更小的问题逐个解决,将结果组合成为最终解返回。
  • 汉诺塔问题解决思想:1、将前n-1个盘子通过c,从a移动到b;2、从a到c移动第n个盘子;3、将前n-1个盘子通过a,从b移动到c。定义函数hanoi(n,a,b,c)表示把a上的n个盘子移动到c上,其中可以遇到b,代码如下:
count = 0

def hanoi(n, a, b, c):
    global count
    if n == 1:
        print('Move', n, 'from', a, 'to', c)
        count += 1
    else:
        hanoi(n - 1, a, c, b)
        print('Move', n, 'from', a, 'to', c)
        count += 1
        hanoi(n - 1, b, a, c)
        print('')

hanoi(3, 'left', 'mid', 'right')
print('steps is', count)
  • 递归方法的时间开销是比较大的,一般要比循环的方式慢得多。
  • 递归优势:能使一个蕴含递归关系且结构复杂的程序简洁精炼,增加可读性;特别是在难于找到从边界到解的全过程的情况下,如果把问题推进一步,其结果仍维持原问题的关系。
  • 递归劣势:嵌套层次深,函数调用开销大;重复计算。

字符串

  • 所谓字符串,就是由零个或多个字符组成的有限序列,一般记为s=a1a2…an(0<=n<=无穷),下列代码可以了解字符串的基本使用。
def main():
    str1 = 'hello, world!'
    # 通过len函数计算字符串的长度
    print(len(str1))  # 13
    # 获得字符串首字母大写的拷贝
    print(str1.capitalize())  # Hello, world!
    # 获得字符串变大写后的拷贝
    print(str1.upper())  # HELLO, WORLD!
    # 从字符串中查找子串所在位置
    print(str1.find('or'))  # 8
    print(str1.find('shit'))  # -1
    # 与find类似但找不到子串时会引发异常
    # print(str1.index('or'))
    # print(str1.index('shit'))
    # 检查字符串是否以指定的字符串开头
    print(str1.startswith('He'))  # False
    print(str1.startswith('hel'))  # True
    # 检查字符串是否以指定的字符串结尾
    print(str1.endswith('!'))  # True
    # 将字符串以指定的宽度居中并在两侧填充指定的字符
    print(str1.center(50, '*'))
    # 将字符串以指定的宽度靠右放置左侧填充指定的字符
    print(str1.rjust(50, ' '))
    str2 = 'abc123456'
    # 从字符串中取出指定位置的字符(下标运算)
    print(str2[2])  # c
    # 字符串切片(从指定的开始索引到指定的结束索引)
    print(str2[2:5])  # c12
    print(str2[2:])  # c123456
    print(str2[2::2])  # c246
    print(str2[::2])  # ac246
    print(str2[::-1])  # 654321cba
    print(str2[-3:-1])  # 45
    # 检查字符串是否由数字构成
    print(str2.isdigit())  # False
    # 检查字符串是否以字母构成
    print(str2.isalpha())  # False
    # 检查字符串是否以数字和字母构成
    print(str2.isalnum())  # True
    str3 = '  jackfrued@126.com '
    print(str3)
    # 获得字符串修剪左右两侧空格的拷贝
    print(str3.strip())


if __name__ == '__main__':
    main()
  • 字符串(String)是一个字符的序列,表示时需要使用成对的单引号或双引号括起来。也可以使用成对的三引号,这种情况下会保留字符串中的所有格式信息。想要在字符串中输入引号的话,需要在引号前加入转义字符“\”。
  • 拼接:”+“,将两个字符串拼接起来,但不能拼接整数,除非也用引号括起来。
    重复:“*”,字符串乘以整数(位置可以互换),表示重复整数次。
  • 成员运算符in:判断一个字符串是否为另一个字符串的子串,返回值为Ture或False,示例如下,x的值即为False。
name = 'hello world'
x = 'a' in name
print(x)
  • for语句:枚举字符串的每个字符,示例如下,结果就会每个字符包括空格在内各占一行,顺序打印出来。
name = 'hello world'
for x in name:
    print(x)
  • for与in的练习:输入一串字母,将其中为元音的字母打印出来,实现代码如下,输入若为abcde,则打印结果即为ae。
def vowles_count(s):
    for c in s:
        if c in 'aeiouAEIOU':
            print(c, end="")
a = input()
vowles_count(a)
  • 字符串索引:字符串中每个字符都有一个索引值(下标),索引从0、1…(前向后)或-1、-2…(后向前)开始。
    索引运算符[]:[]中的数字代表获得字符串中第几个字符,要注意索引的开始值。
  • 切片:用来选择字符串的子序列,语法为[start:finish],其中start为子序列开始位置的索引值,finish为子序列结束位置的下一个字符的索引值。如果不提供start或finish,默认start为第一个字符开始,finish为最后一个字符。finish可以超出字符串的范围,也表示到字符串的尾部。
  • []中还可以写入第三个参数,为计数参数,即[start:finish:countBy],默认countBy为1,即获得连续的子字符串,如果提高countBy的值到2,则会每次跳过一个字符,以此类推。countBy也可以为负数,则获得逆字符串,但此时要注意两个参数的取值,可以两个都不填,意味着从尾到头获得该字符串。
  • 字符串一旦生成是不可变的,如果想要改变字符串中的某一个内容,则需通过切片等操作获得一个新的字符串,可以选择将新字符串赋值给原字符串,也可以使用字符串方法。
  • 字符串方法:即对象提供的函数,如变量名.replace(old,new)方法,即生成一个新的字符串,使用new替换old子串,示例如下,这样s中所有的e都变成了a。
s = 'hello world'
s = s.replace('e', 'a')
print(s)
  • 更多字符串方法:find(‘字符’)方法,即找到第一次出现该字符的下标;split()函数,括号中若无参数,则是在字符串空格的位置对原始字符串进行切分。关于字符串的方法,可以使用dir(str)函数来浏览。
  • 文件操作:
    打开文件:f = open(filename,mode),其中mode有r(读,默认),w(写)等。
    按行读取文件内容:for line in f:pass。
    关闭文件:f.close()。
    写文件:f.write(str)。
  • 练习:读取人名列表文件names.txt,将每个人名转换为首字母大写,其他字母小写的格式,实现代码如下,其中人名文件与代码文件注意放在同一存储空间。
    strip()函数可以用来去掉一个字符串开头结尾的空格回车等。
    title()函数可以将字符串转化为首字母大写,其他字母小写的格式。
f = open('names.txt', 'r')
for line in f:
    line = line.strip()
    print(line.title())
f.close()
  • 字符串大小用字典序来表示:
    首先比较两个字符串的第一个字符。
    如果相同则比较下一个,如果不同则由第一个字符决定大小关系。
    如果其中一个字符为空(较短),则其更小。
  • 字符串格式化使用format()方法:
    format()中的参数都用来替换{}的内容,{}的格式为{field name:align width.precision type},比如{:9.4f},即占据9个字符,并以4位的小数精度输出{}中的值,示例如下。
    如需调用math包中的pi,则需先声明math包,即import math。
>>>print('PI is {:9.4f}'.format(3.1415926))
PI is    3.1416
  • 判断一个人名是否满足下列模式:
    判断其是否为Michael,则有Michale : name == ‘Michael’。
    是否以Mi开始:name[:2] == ‘Mi’。
    是否包含cha子串:‘cha’ in name。
    但如需判断以下情况:是否包含c?a子串(即c与a中间隔字符不确定),是否包含c*e子串(即表示任意多个字符),则需要用到正则表达式。
  • 正则表达式用来描述字符串的模式:
    .表示任意字符。
    \d+表示一系列数字。
    [a-z]表示一个小写字母。
    正则表达式还包含很多内容,不再赘述。
  • 练习:判断一个人名是否含有C.A模式,即C与A之间相隔任意字符,代码如下。
import re
f = open('names.txt')
for line in f:
    line = line.strip()
    pattern = 'C.A'
    result = re.search(pattern, line)
    if result:
        print('Name is {}'.format(line))
f.close()