01Python基础

319 阅读37分钟

一、Python概述

这是我参与更文挑战的第4天,活动详情查看: 更文挑战

1.1 Python的特点

  1. Python是一们面向对象的语言,在Python中一切皆对象
  2. Python是一们解释性语言
  3. Python是一们交互式语言,即其在终端中进行命令编程
  4. Python是一们跨平台的语言【没有操作系统 的限制,在任何操作系统 上都可以运行Python代码】
  5. Python拥有强大和丰富的库,又被称为胶水语言,能把其他语言(主要C/c++)写的模块很轻松的结合在一起

1.2 Python的优缺点

  • 优点:
    • 易于维护和学习
    • 广泛的标准库【提供了大量的工具】
    • 可扩展性强
    • Python对于大多数据库都有相应的接口【Mysql、sqliters3、MongoDB、Redis等】
    • 支持GUI编程【图形化界面】
  • 缺点:
    • 和C语言相比较Python的代码运行速度慢
    • 代码无法加密

1.3 Python代码的执行过程

流程:先把源码编译为字节码(.pyc) --> Python虚拟机 --> 执行编译好的字节码 --> Python虚拟机将字节码翻译成相对应的机器指令(机器码)。

Python程序运行时,先编译字节码并保存到内存中,当程序运行结束后,Python解释器将内存中的字节码对象写到.pyc文件中。

第二次再执行时,先从硬盘中寻找.pyc文件,如果找到,则直接载入,否则重复上面过程。

优点:不重复编译,提高执行效率

二、Python中的基础语法

2.1 Python中变量在内存中的存储

2.1.1 引用和对象

  • 对象:当创建数据对象时,在内存中会保存对象的值,这个值就是对象自己

  • 引用:对象保存在内存空间,外部想要使用对象的值,就需要使用引用来操作对象。内存中会保存对象引用的数量,当某个对象的引用为0时,对象就会被回收。

2.1.2 可变数据类型和不可变数据类型

  • 可变数据类型

    可变数据类型:允许变量的值进行改变,对于变量给值的过程中,只是改变了变量的值,而不会新建一个对象,所以可变数据类型的意思就是说对一个变量进行操作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的内容变化了或者地址得到了扩充。
    
  • 不可变数据类型

    对象本身的值不可变,如果改变了变量的值,相当于新建一个对象,而对于相同的值的对象,在内存中只有一个对象
    

可变数据对象:列表(list)、字典(dict)

不可变数据对象:整形(int)、浮点型(float)、字符串(str)、元组(tuple)

:此处的可变和不可变,是指内存中的对象(value)是否可以改变,对于不可变类型的对象,在对对象进行操作的时候,必须要在内存中重新开辟一块新的区域。对于可变类型的对象,在对对象进行操作的时候,不会重新申请新的地址,而是在该 对象的地址后面继续申请即可。

2.1.3 引用传递和值传递(函数传值)

说明:可变对象为引用传递,不可变对象为值传递。

  • 引用传递:传递列表或者字典时,如果改变引用的值,就修改了原始的对象

    def check(a): # 因为列表是可变对象
        print(a)
        print(id(a))
        a.append([1, 2, 3])
        return a
    
    
    a = [3, 4, 5]
    print(check(a))
    print(id(a))
    
    """
    [3, 4, 5]
    44909288
    [3, 4, 5, [1, 2, 3]]
    44909288
    """
    
  • 值传递:当传递不可变对象时,如果改变引用变量的值,只是创建了不同的对象,原始对象并没有改变

    def check1(a):  # 因为字符串是不可变对象,所有重新创建了地址
        print(a)
        print(id(a))
        a = "i am test"
        print(id(a))
        return a
    
    
    a = "This is aa"
    print(check1(a))
    print(id(a))
    """
    This is aa
    58547040
    58547120
    i am test
    58547040
    """
    

2.1.4 深拷贝和浅拷贝

  • 浅拷贝:语法:copy.copy(),浅拷贝是创建了一个和原对象一样的类型,但是其内容是对原对象元素的引用。

    ​ 1.对于可变数据,不可变数据类型来说浅拷贝复制是内存引用

    浅拷贝只拷贝第一层,深层次的数据改变都会影响其他.

    a = [9, 8, [1, 2], 6] # 浅拷贝是拷贝父对象的引用,无论a,b那方都会改变地址
    b = a.copy()
    b = a[:] # 也表示浅拷贝
    print(a, b)
    a.append(2)
    b.append(3)
    print("第一层:", a, b)  # 没有发生改变
    a[2].append(3)
    b[2].append(4)
    print("第二层:", a, b)  # 发生改变
    """
    [9, 8, [1, 2], 6] [9, 8, [1, 2], 6]
    [9, 8, [1, 2], 6, 2] [9, 8, [1, 2], 6, 3]
    [9, 8, [1, 2, 3, 4], 6, 2] [9, 8, [1, 2, 3, 4], 6, 3]
    """
    
  • 深拷贝:语法:copy.deepcopy(),在内存中将所有的数据重新建立一份。

    ​ 1.对于可变的数据类型来说,深拷贝也会将嵌套内容进行重新开辟

    ​ 2.对于不可变数据类型来说,深拷贝同样复制的内存的引用

    """
    深拷贝就是在内存中重新开辟一块空间,不管数据结构有多复杂,只要数据发生改变,就重新开辟一块内存空间把内容复制下来,直到最后一层,通俗点讲就是,深拷贝就是重新定义一个变量,于之前的没有半毛钱关系,所以更改里面的内容,原来的并不会发生改变。
    """
    
    import copy
    
    a = {1: [1, 2, 3]}  # 深拷贝会重新开辟一个内存空间
    c = copy.deepcopy(a)
    print(a, c)
    a[1].append(4)
    c[1].append(5)
    print(a, c)
    """
    {1: [1, 2, 3]} {1: [1, 2, 3]}
    {1: [1, 2, 3, 4]} {1: [1, 2, 3, 5]}
    """
    

变量的本质:在内存中开辟一块空间,其中存储了指定类型的数据【实体(对象)存储在堆中,变量(引用)存储在栈空间中】

2.2 基本数据类型

Python中常用的数据类型有:整型(int)浮点型(float)字符串(str)空值(None)布尔值(True/False)复数(complex)函数列表(list)元组(Tuple)集合(set)字典(Dictionary)

  • **不可变数据(3 个):**Number(数字)、String(字符串)、Tuple(元组);
  • **可变数据(3 个):**List(列表)、Dictionary(字典)、Set(集合)。

Python中常量的命名规则使用变量名全大写。(STATIC="A")

Python中的变量不需要声明,每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。(a = b = c = 1 ,a, b, c = 1, 2, "a")

2.2.1 Python数据类型转换

函数描述
chr(x)将一个整数转换为一个字符
int(x)将x转换为一个整数
ord(x)将一个字符转换成对应的acsii码
......

2.3 关键字、标识符和内置函数

2.3.1 关键字

关键字:在Python中被赋予了特殊含义的英文单词

Python中的关键字的查看使用keyword模块

import keyword

print(keyword.kwlist)
"""
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
"""

2.3.2 标识符的命名规则

标识符的命名规则:标识符的命名构成:数字、字母、下划线,但不能以数字开头,在标识符的命名中严格区分大小写

命名规范

Python官方:所有单词全部小写,不同单词之间使用下划线分割,普遍是用驼峰命名法(小驼峰与大驼峰)

2.3.3 内置函数

import sysprint(dir(sys.modules))

内置函数表;

内置函数
abs()dict()help()min()setattr()
all()dir()hex()next()slice()
any()divmod()id()object()sorted()
ascii()enumerate()input()oct()staticmethod()
bin()eval()int()open()str()
bool()exec()isinstance()ord()sum()
bytearray()filter()issubclass()pow()super()
bytes()float()iter()print()tuple()
callable()format()len()property()type()
chr()frozenset()list()range()vars()
classmethod()getattr()locals()repr()zip()
compile()globals()map()reversed()import()
complex()hasattr()max()round()
delattr()hash()memoryview()set()

2.4 Python运算符

2.4.1 算术、赋值运算符

算术运算符

** 求幂运算

// 整除运算或者成为地板除

% 取余运算

常用算数运算符的优先级:** -> * -> / // % -> + -

赋值运算符

赋值运算符:=

复合运算符:+= -= *= /=

关系比较:== != >= <=

2.4.2 逻辑运算

运算顺序:() => not => and => or

运算符逻辑表达式描述
andx and y布尔"与" x,y都为True,返回True
orx or y布尔"或" x,y一个为True,返回True
notnot x布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。
算符描述
&按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
|按位或运算符:只要对应的二个二进位有一个为1时,结果位就为1。
按位异或运算符:当两对应的二进位相异时,结果为1
~按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1。~x 类似于 -x-1。(运算原理:计算补码,按位取反,转为原码,末尾加1)
<<左移动运算符:运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
>>右移动运算符:把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数

QQ图片20210516150819

0表示False 1表示True

2.4.3 成员及身份运算

成员运算符:in not in (在其中,不在其中)

身份运算符:is is not(是,或者不是)

2.5 Python中的语句

2.5.1 if语句

if语句在Python中的两种结构为:

  # 1
  if 条件表达式:
      代码块
  
  # 2
  if 条件表达式:
      代码块
  elif 条件表达式:
      代码块
    
  # 3
  if 条件表达式:
      代码块
  else:
      代码块

2.5.2 for语句

while 条件表达式:		代码块
# 9*9乘法表
line = 0
while line < 10:
    temp = 1
    while temp <= line:
        print(temp, "*", line, "=", temp * line, end=" ")
        temp += 1
    print(" ")
    line += 1
"""
1 * 1 = 1  
1 * 2 = 2 2 * 2 = 4  
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9  
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16  
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25  
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36  
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49  
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64  
1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81 
"""

break 直接跳出当前循环

continue 结束当前正在执行的循环,继续下一次循环

三、Python内置数据结构

Python中最基本的数据结构是序列(sequence),python包含6种内建的序列,包括列表、元组、字符串、Unicode字符串、buffer对象和xrange对象

3.1 字符串(str)

3.1.1 字符串的创建

将文本放在单引号、双引号和三引号之间

str1 = ' hello, fanison 'str1 = “ hello, fanison ”str1 = """hello word"""

3.1.2 字符串的转义

原生字符串:使用r

str1 = r"hello, fanison"	

3.1.3 字符串的切片操作

顾头不顾尾

  • 索引运算符s[i] 返回一个序列的元素i

  • 切片运算符s[i:j] 返回一个在字符串中从第i个元素到j-1的元素

  • 扩展切片运算符 s[i:j:step] 在字符串中以step的步长从第i个到j-1个元素之间的元素,step为正数表示正着取,i<i或者说s[i,-1]倒着取

    len(s) s中的元素个数

    min(s) s的最小值

    max(s) s的最大值

a = [1, 2, 3, 4, 5, 6, 7, 8]
print(a[2:])  # 下标从0开始
print(a[2:5])
print(a[2:7:2])
print(a[2:-1])
print(a[::-1])  # 反转列表
print(a[-1::-1]) # 反转列表
"""
[3, 4, 5, 6, 7, 8]
[3, 4, 5]
[3, 5, 7]
[3, 4, 5, 6, 7]
[8, 7, 6, 5, 4, 3, 2, 1]
"""

3.1.4 字符串的相关方法

s.index(sub [,start [,end]]) 找到指定字符串sub首次出现的位置,找不到就报错

s.find(str,beg=0,end=len(string)) 找到字符串sub首次出现位置,与index不同是不报错而返回-1

s.upper() 将一个字符串转换为大写形式

s.lower() 将一个字符串转化为小写形式

s.join(t) 使用s作为分隔符连接序列t中的字符串 s.strip() 将s两边不显示的符号去掉之后返回(lstrip、rstrip)

s.split(t) 获取以字符t将s切割的列表

s.encode() 获取s的指定编码的bytes值

bytes.decode() 获取bytes的对应编码的字符串,在python2中使用decode函数

s.endswith(suffix,beg=0, end=len(string)) 检查中是否以suffix字符串结尾

x = "do you fuck me?"
print(x.index("y"))  # 找不到就报错
print(x.find("z"))  # 找不到就返回-1
print(x.upper())
print(x.lower())
print(x.join("-x"))
print(x.split(" "))
print(x.encode())
print(x.endswith("f"))  # 判断结尾是不是以f
"""
3
-1
DO YOU FUCK ME?
do you fuck me?
-do you fuck me?x
['do', 'you', 'fuck', 'me?']
b'do you fuck me?'
False
"""

3.2 列表(list)

变量:相当于是一个窗口,每次只能存储一个数据

作用:相当于是一个窗口,可以同时存储多个数据

本质:一种有序的集体【有序:数据的存放顺序和底层存储的顺序是相同】

列表:允许存放重复元素,允许存放不同类型的数据

list1 = [45,23,2,54,54,6,"hello",4637,False]list2 = [1, 2, 3]

列表元素的访问:从0开始

  • 下标,索引
  • 切片
  • 判断
  • len:长度 max():最大值 min():最小值 index():下标
# 通过索引访问
print(list1[0])
print(list1[-1])
# print(list1[1000]) 列表下标越界

# 切片 格式:列表名[start:end:step],包头不包尾
print(list3[2:]) # 获取从开头到指定下标的元素。。。。
print(list3[2:6]) # 截取指定区间
print(list3[4:100]) # 特殊情况1:如果end超出了下标的范围,则默认获取从指定下标开始到结尾的元素 等价于print(list3[4:])
print(list3[1:6:2])  #1,3,5#特殊情况2:step在默认情况下为1
print(list3[-1:-6])  #当start和end都为负数,step为正数的时候,获取的结果为[]
#将列表倒序输出 当start,end和step都为负数的时候,表示倒序
print(list3[-1::-1])
print(list3[::-1])
print(list3[-1:-6:-2])  

print(2 in list2)  # 判断某个元素是否在列表中,true,false
print(2 not in list2)

# 3.1len(),获取列表长度
print(len(list1))
# 3.2max(),获取列表中的最大值
print(max(list1))
# 3.3min(),获取列表中的最小值
print(min(list1))
# 3.4index(),获取指定元素在原列表中第一次匹配到的索引
print(list1.index(11))  # 11在list1列表的索引

列表元素的替换/修改

  • 通过下标
list1[0] = 99
print(list1)
print(list1 + list2)  # 生成一个新的列表
print(list2 * 3)  # 列表元素的重复

添加元素

  • append:追加,在列表的末尾添加元素
  • extend:扩展,在列表的末尾添加元素
  • insert:在指定位置插入
l1 = [22, 33, 44, 55]
# 1.1append,追加,在列表的末尾添加元素                  常用
l1.append(66)  # 追加单个元素
# l1.append(77,88)  #报错:TypeError: append() takes exactly one argument (2 given)
l1.append([77, 88])  # 追加多个元素,不能直接追加,通过列表的形式追加,形成了一个二维列表

# 1.2extend,扩展,在列表的末尾添加元素
l2 = [22, 33, 44, 55]
# l2.extend(66)   #T报错:ypeError: 'int' object is not iterable
l2.extend([66])  # 追加单个元素,不能直接添加,参数一定是可迭代的
l2.extend([77, 88])

# 1.3insert,插入,在指定索引处插入一个元素,后面的元素向后顺延
# 列表名.insert(索引,被插入的元素)
l3 = [22, 33, 44, 55]
l3.insert(2, 66)  # 插入单个元素
l3.insert(1, [77, 88])  # 插入多个元素:和append类似,将整个列表直接插入 

"""
总结:
列表名.功能名(xx)
append可以直接添加单个元素,而extend不能
append在添加多个元素的时候,是以列表的形式添加,而extend只添加元素【打碎加入】
insert():在指定位置插入元素
""""""

删除

删除元素

  • pop()
  • remove()
  • clear
# pop,弹出,移除并获取列表中指定索引处的元素    在栈中【列表的底层工作原理是栈】

list1 = [11, 22, 33, 44, 55]
# 1.pop() 注意:pop在默认情况下删除的是最后一个元素
result1 = list1.pop()
print(list1)
print(result1) # 删除的元素
result2 = list1.pop(2) # 删除下标为2的
print(list1)

# 2.remove() 移除,直接操作的是元素
list2 = [11, 22, 33, 44, 55, 33, 33, 55]
list2.remove(22)
print(list2)

# 注意:移除指定元素在列表中第一次匹配到的元素【从左往右】
list2.remove(33)
print(list2)

# 3.clea()r   清除,将指定列表变为空列表      使用场景:循环中,每次需要清空【重置】列表
list2.clear()

其它用法

  • reverse 反转列表
  • sort() 排序(内部进行排序,不生成新列表)
  • sored() 排序(生成了一个新的列表)
  • copy() 在列表中
    • list2 = list1 # 不新开辟内存空间
    • list2 = list1.copy() #开辟一个新的内存空间
  • 转换list() tuple()
# 4.其他用法
# 4.1.reverse,反转
list1 = [35, 4, 5, 4, 654]
print(list1[-1::-1])
list1.reverse()
print(list1)

# 4.2.1sort,排序,默认为升序,在列表内部进行排序
# 列表名.sort()
list1.sort()  # 升序
list1.sort(reverse=True)  # 降序

# 4.2.2sorted,排序,默认为升序,生成了一个新的列表
# sorted(列表名)
newList = sorted(list1)  # 升序
newList2 = sorted(list1, reverse=True)  # 降序

# 4.3copy,拷贝
list1 = [23, 54, 56]
list2 = list1  # 没有重新开辟内存空间,
list2[1] = 100
print(list1)
print(list2)
print(id(list1) == id(list2))

# copy,深拷贝,堆空间层面上的拷贝【实体】
list1 = [23, 54, 56]
list2 = list1.copy()  # 重新开辟内存空间,但是只有第一层
list2[1] = 100
print(list1)
print(list2)
print(id(list1) == id(list2))

# 4.4转换
# list(),tuple()

# 二维列表:遍历
l1 = [[11, 22, 33, 44], [44, 55, 66]]
print(l1[0])  # l1[0] = [11, 22, 33, 44]
print(l1[0][2])

for i in l1:
    for j in i:
        print(j)

面试题

list1 = [54, 6, 57, 5, 57, 7, 6, 7,22, 57, 57, 57, 436]
# 模拟index
for i in range(len(list1)):  
    if list1[i] == 22:
        print(i)
               
# count(),统计个数,统计指定元素在列表中出现的次数
print(list1.count(22))  # 统计22在列表中出现的次数


# 删除所有的57
for i in list1[:]: # 这里一定要注意
    if i == 57:
        list1.remove(57)
print(list1)

# 模块copy ,copy(),deeepcopy()【面试题】
# 内容角度上的拷贝,copy只拷贝最外层,deepcopy可以拷贝内层的内容【二维列表中】
a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]  

d = copy.copy(c)  # 浅拷贝,但用的还是父的引用
print(id(d) == id(c))  # False

e = copy.deepcopy(c) # 深拷贝
print(id(e) == id(c))  # False

a.append(4)
print(c)  # [[1, 2, 3, 4], [4, 5, 6]]
print(d)  # [[1, 2, 3, 4], [4, 5, 6]] # 浅拷贝只第一层,这里是第二层
print(e)  # [[1, 2, 3], [4, 5, 6]]

"""
d:[[1, 2, 3], [4, 5, 6]]
[list1,list2]---->list1:[1,2,3]  list2:[4,5,6]
""""

3.3 元组(tuple)

和列表类似,本质是一种有序的集合(但是元组没有增删改)

元组和列表的不同之处:

  • 定义不同:列表[] 元组()
  • 是否能够修改:列表可以进行增加或者删除元素的操作,但是,元组一旦被定义之后,其中的元素将不能进行任何的更改
  1. 允许存储重复元素,允许存储不同类型的数据
t1 = (1, 1, 2, 3, 4, 5, "Hello", False)
  1. 当元组中只有一个元素的时候,会识别为一个普通变量,我们要在后面加一个,元组名=(元素,)
t = (10,)
  1. 特殊情况:如果在元组中的元素是列表,则列表中的元素依旧可以修改
依据:元组和列表中存储都是变量的地址,元素不允许修改,只需要保证未发生改变即可其中的地址
t5 = (23, 5, 3, 5, [235, 345, 5, 45, 4])
print(t5)
print(t5[4])  # [235,345,5,45,4]
t5[4][1] = 100
print(t5)
  1. 其它用法
# 列表和元组的遍历
# 直接获取元素
for element in t5:
    print(element)

# 获取索引
for i in range(len(t5)):
    print(i, t5[i])

# 同时遍历索引和元素
# 需要将元组或者列表转换为枚举类型
# 注意:下面的i并不是元组或者列表的索引,而是枚举中的编号
for i, element in enumerate(t5):
    print(i, element)

元组元素的访问

元组名[索引]

print(t1[5])

3.4 字典(dict)

字典:也是一种存储数据的方式,但是字典是无序的(其它方法和列表类似)。

类似于list或者tuple,但是,字典采用键-值对的方式存储数据

作用:具有极快的查找速度

键【key】相当于list或者tuple中的索引

key的特点

  1. 字典的key是唯一的【key不允许重复】
  2. key必须为不可变的数据

list是可变的,不能用来当做key

tuple,数据型,字符串,布尔值都是不可变的,可以被充当key

dict1 = {"zhangsan": 10, "lisi": 13, "zhaoliu": 50}

  • 字典名["key"] 如果key不存在,则报错
  • 字典名.get("key") 如果key不存在,返回None,不会报错
# 1.访问键值对
print(dict1["lisi"])
# 访问一个不存在的key,则报错
# print(dict1["abc"])  #KeyError: 'abc'

# 2.get()
result1 = dict1.get("zhaoliu")
print(result1)
# 如果key不存在,则不会报错,返回None,一般用于判断
result2 = dict1.get("def")
print(result2)

  • 字典名[key] = value
dict1["zhaoliu"] = 100result0 = dict1["zhaoliu"]print(result0)

  • 字典名[key] = value
# 注意:如果key存在,则表示修改value的值;如果key不存在,则表示在字典中添加一对新的键值对dict1["abc"] = 20print(dict1)

删除

  • dict1.pop("key") 通过key删除
  • del dict1["key"] 通过key删除
  • del dict1 删除整个字典
  • dict1.clear() 清空字典里面的元素

注意:删除指定的key,对应的value也会随着删除

dict1.pop("lisi")dict1.clear()     # 清空字典del dict1['zhaoliu'] #删除key为zhaoliu的del dict1         # 删除字典print(dict1)

字典的遍历

  • 直接遍历就是遍历的key
  • dict.values() 值
  • enumerate(dict) 编号和key
  • dict.items() 同时遍历key和value
# 3.字典的遍历
# 3.1直接遍历key         掌握
for key in dict1:
    print(key, dict1[key])
for key in dict1.keys():
    print(key, dict1[key])

# 3.2直接遍历value
for value in dict1.values():
    print(value)

# 3.3,遍历的是键值对的编号和key
for i, element in enumerate(dict1):
    print(i, element)

# 3.4同时遍历key和value               掌握
for key, value in dict1.items():
    print(key, value)

面试题

"""
【面试题:dict和list之间的区别】
1.dict查找和插入的速度不会因为key-value的增多而变慢,
  而list在每次查找的时候都是从头到尾进行遍历 ,当数据量大的时候,list速度肯定会变慢
2.dict需要占用大量的内存空间,内存浪费多,
 而list只相当于存储了字典中的key或者value,并且list数据是紧密排列的
# 1.逐一显示列表l1 = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]中索引为奇数的元素
l1 = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
for i, index in enumerate(l1):
    if i % 2 != 0:
        print(i, index)

# 2.将属于列表l1 = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],但不属于列表l2 = ["Sun","Mon","Thu","Fri","Sat"]的所有元素定义为一个新列表l3
l1 = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
l2 = ["Sun", "Mon", "Thu", "Fri", "Sat"]
l3 = []
for i in l1:
    if i not in l2:  # 判断i不在l2中就添加到l3中
        l3.append(i)
print(l3)

s = set(l1)
s1 = set(l2)
print(s ^ s1)

# 3.已知列表namelist=['stu1','stu2','stu3','stu4','stu5','stu6','stu7'],删除列表removelist=['stu3', 'stu7', 'stu9'];请将属于removelist列表中的每个元素从namelist中移除(属于removelist,但不属于namelist的忽略即可);
namelist = ['stu1', 'stu2', 'stu3', 'stu4', 'stu5', 'stu6', 'stu7']
removelist = ['stu3', 'stu7', 'stu9']
for i in removelist:
    if i in namelist:  # 在里面的话,就用remove()删除
        namelist.remove(i)
print(namelist)

# 4.有一个字符串是一句英文,统计每个单词出现的次数,生成一个字典,单词作为key,次数作为value生成一个字典dict1
str1 = "today is a good day today is a bad day today is a nice day"
str2 = str1.split(" ")  # 以空格分割,成列表
dict1 = {}
for i in str2:
    c = dict1.get(i)  # key:单词 value:次数  用.get()获取不到的话就为None(不会报错)
    if c == None:
        dict1[i] = 1  # 添加到字典
    else:
        dict1[i] += 1
print(dict1)

# 5.已知列表list1 = [0,1,2,3,4,5,6],list2 = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],以list1中的元素作为key,list2中的元素作为value生成一个字典dict2
list1 = [0, 1, 2, 3, 4, 5, 6]
list2 = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
# 定义一个变量,作为list1和list2的索引
index = 0
dict2 = {}
if len(list1) == len(list2):  # 如果两个列表长度相等
    while index < len(list1):
        dict2[list1[index]] = list2[index]
        index += 1
elif len(list1) > len(list2):  # 如果两个列表长度不相等
    while index < len(list2):
        dict2[list1[index]] = list2[index]
        index += 1

    else:
        while index < len(list1):
            dict2[list1[index]] = list2[index]
            index += 1
print(dict2)

x = dict(zip(list2,list1))

3.5 集合(set)

集合:不允许重复元素,而且进行交集以及并集的运算

表示:{} 和dict之间的关系:set中只是存储了key

本质:无序且无重复元素的集合

  • add() 添加
    • 单个元素,如果添加的元素存在,则添加失败,不报错
    • 多个元素,在set中,使用add添加,则只能添加元组,不能添加list和dict
  • update() 更新
    • 不能直接添加,参数可以是列表,元组,字典等(添加字典的话,只会把key添加进去)
# add(),添加,
set1 = {11, 22, 33, 44, 55}
# 单个元素
set1.add(66)
set1.add(55) # 如果添加的元素存在,则添加失败,不报错
# 多个元素
# s1.add([77,88])   #TypeError: unhashable type: 'list'
s1.add((77, 88))
# s1.add({1:"a"})
# 结论:在set中,使用add添加,则只能添加元组,不能添加list和dict


# update(),更新,update的参数只能是可迭代对象【打碎加入】
set2 = {11, 22, 33, 44, 55}
# set2.update(66)   #报错:TypeError: 'int' object is not iterable
set2.update([66])  # []
set2.update((77, 88))  # ()
    set2.update({"12": 14, "13": 13})  # 只会把key添加进去
    set2.update("hgjhgaa")  # 把字符个个的添加进去(不会把重复的添加进去)

删除

  • remove() 如果删除的元素不存在,则会发生错误.
  • discard() 如果删除的元素不存在,不会发生错误.
  • pop() 随机删除集合中的一个元素
  • clear() 清空集合
set2.remove(77)set2.discard(33)set2.pop()set2.clear()

交集和并集

# 交集和并集
s1 = {3, 54, 4, 5, 7}
s2 = {3, 54, 4, 8, 90}
# 交集:&【按位与】
print(s1 & s2)
# 并集:|【按位或】
print(s1 | s2)
rint(s1 ^ s2)

面试题

# 掌握:去除列表中的重复元素
s2 = set([3, 46, 5, 65, 7, 65, 7])  # []
s3 = set((2, 43, 54, 5, 4, 5))  # ()
s4 = set({10: "a", 20: "b"})  # 字典的话,只有ke

3.6 list,tuple,dict,set的区别和联系

  • 定义方式: list[] tuple() dict{} set{}
  • 有序: list tuple
  • 无序: dict set
  • 是否允许出现重复元素: list,tuple:允许 dict:key不允许,value可以, set不允许
  • 都属于可迭代对象
  • set相当于存储了字典中的key
  • 可以相互转化

四、Python函数总结

4.1、函数的基本用法

4.1.1 概念

函数是对程序逻辑进行结构化或是过程化的一种编程方法,其是组织好的,可重复使用,用来实现单一,或者相同功能的代码段.

函数提高了应用点的模块性和代码的重复利用率

本质:函数是对功能的封闭

形式参数:简称形参,本质是一个没有值的变量.

实际参数:简称实参,本质是常量,变量或者表达式

传参:实参给形参赋值的过程,而实参的类型取决于形参的需要

4.1.2 函数定义

def  函数名(参数1,参数2,参数3.。。。):          
    函数体          
    return 返回值

a、函数命名遵循标识符规则,做到见名知意,小驼峰命名法、

b、参数1,参数2,参数3.....形式参数,不同的参数之间使用逗号隔开,参数的数量没有限制,依据具体的需求决定参数的数量

c、函数体:被封装的功能

d、return:结束函数,将返回值返回给调用者,也可单独使用

e、返回值可为常量、变量、表达式

4.1.3 返回值

返回值:表示一个函数执行完毕之后得到的结果

:对于return语句不带参数,则返回一个None

4.1.4 调用

函数的调用:实质就是函数入栈出栈的过程

即:函数的入栈:函数被调用;函数的出栈:函数被调用完毕

:在函数调用的过程要注意避免出现死循环

4.1.5 变量的作用域

变量的作用域:指变量可以被访问的范围

作用域的划分

L:(local)局部作用域,最内层,包含局部变量,比如一个函数/方法内部。

E:(Enclosing)函数作用域(闭包),nonlocal(必须使用在闭包中)

G:(Global)全局作用域,当前脚本的最外层,比如当前模块的全局变量。

B:(Built-in)内置作用域,包含了内建的变量/关键字等。,最后被搜索

变量的查找规则(变量重名)

python中变量的作用域由大到小,依次为内建(built_in B) >全局(glbal G)> 函数的闭包外(enclosing E)> 局部(local L)

:在变量重名情况下在函数内部访问变量时使用就近原则。

如果将全局变量的名字声明在一个函数体内的时候,全局变量的名字能被局部变量给覆盖掉,此时我们就需要使用global或者nonlocal来声明变量了。

全局变量和局部变量

total = 0  # 这是一个全局变量


# 可写函数说明
def sum(arg1, arg2):
    # 返回2个参数的和."
    total = arg1 + arg2  # total在这里是局部变量.
    print("函数内是局部变量 : ", total)
    return total


# 调用sum函数
sum(10, 20)
print("函数外是全局变量 : ", total)

global和nonlocal关键字

当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了

global

num = 1


def fun1():
    global num  # 需要使用 global 关键字声明
    print(num)  # 1
    num = 123
    print(num)  # 123


fun1()
print(num)  # 123,因为函数内部使用了global修改了变量

nonlocal (必需使用在闭包中)

如果要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量则需要nonlocal关键字了.(闭包)

def outer():
    num = 10

    def inner():
        nonlocal num  # nonlocal关键字声明
        num = 100
        print(num)

    inner()
    print(num)  # 100 因为用nonlocal修改了局部变量


outer()

4.1.6 参数

  1. 参数的传递

参数的传递有值传递引用传递

  • 值传递:传递不可变类型的数据,例:num,str,tuple等,在值传递时,形参的改变并不会影响实参
def check(s):
    print(s)
    print(id(s))
    s = "i am test"
    print(id(s))
    return s


s = "This is a test"
print(check(s))
print(s)
  • 引用传递:传递可变类型的数据,例:list,dict,set等;形参的改变会影响实参的使用

    # 引用传递:传递列表或者字典时,如果改变引用的值,就修改了原始的对象
    def check(l):    
        print(l)   
        print(id(l))    
        l.append([1, 23, 2, 3, 4])    
        return ll = [1, 2, 3, 4]
      print(check(l))
      print(id(l))
    '''[1, 2, 3, 4]53235528[1, 2, 3, 4, [1, 23, 2, 3, 4]]53235528'''
    

在函数中参数传递的是对象的引用

  1. 参数的类型

    • 必须参数:必备参数须以正确的顺序传入函数,调用时的数量必须和声明时的一样.

      def printme(str):    
          "打印任何传入的字符串"    
          print(str)    
          return# 调用printme函数printme("aa")  # 必须参数
      
    • 关键字参数:允许实参的顺序和形参的顺序不一致,因为Python解释器会根据关键字参数的名称自动的匹配

def show2(name, age):
    print("name:%s age:%d" % (name, age))


show2(name="jack", age=47)
# 注:关键字参数使用在实参列表中,不要求顺序保持一致
show2(age=36, name="fadj")
# 注:在实参列表中,可以不全部设置为关键字参数
show2("zhangsan", age=15)


# 注:关键字参数只能出现在实参列表的后面
def show3(a, b, c):
    print(a, b, c)


show3(1, 4, 4)
show3(a=3, c=5, b=5)
show3(45, b=9, c=18)
# show3(a=45,9,18)  #SyntaxError: positional argument follows keyword argumen
  • 默认参数:调用函数的时候,如果没有传递参数,则使用默认值[default]
# 注意1:默认参数体现在形参列表中
def func3(name="abc", age=18):
   print("name:%s age:%d" % (name, age))


# 注意2:使用了默认参数,则可以选择不传参,使用的默认值,如果传参,则相当于给形参重新赋值
func3()
func3("jack", 19)
  • 不定长参数:可以处理比声明时更多的参数 *args **kwargs

    • *args,是以元组的形式
    • **wkargs是以字典的形式
    def text(*args, **kwargs):    
        print(args) # 都是接收一个或多个参数    
        print(kwargs)
    text(10, 11, {"a": 1, "b": 2}, x=12, y=13)
    
    """(10, 11, {'a': 1, 'b': 2}){'x': 12, 'y': 13}"""
    

4.1.7 匿名函数

lambda表达式:python中的匿名函数主要用来处理一些简单逻辑表达式的封装,使用lambda关键字

优点:不占用内存,提高代码的运行速度

一般格式为:

var = lambda args:表达式

例如:

# 可写函数说明
sum = lambda arg1, arg2: arg1 + arg2
 
# 调用sum函数
print "相加后的值为 : ", sum( 10, 20 )
print "相加后的值为 : ", sum( 20, 20 ))
  1. 普通匿名函数

    abc = lambda a,b:a+bx = abc(5,5)print(x)
    
  2. 匿名函数作为参数

    def func(x, y, func): # 这里的func其实就是匿名函数的表达式
     print(x, y)
     print(func)
     s = func(x, y)
     print(s)
    
    

func(1, 2, lambda a, b: a + b))


3. 匿名函数与内置函数的结合使用

```python
list2 = [{'a': 10, 'b': 2}, {'a': 50, 'b': 3}, {'a': 1, 'b': 1}, {'a': 89, 'b': 1}]
# max()
m = max(list2, key=lambda x: x['a'])  # 找列表嵌套字典,a的最大值
print(m)
# sorted() 升序
print(sorted(list2, key=lambda x: x["a"], reverse=True))
# filter() 条件 查找a大于10的
result = filter(lambda x: x["a"] > 10, list2)
print(list(result))
# reduce   (有时必需导入包)  reduce(function, sequence[, initial]) -> value
from functools import reduce

tuple1 = (3, 4, 6, 1, 2, 3)
result = reduce(lambda x, y: x * y, tuple1, 50)  # 3*4*6*1*2*3*50对元组中的每一数依次相乘
result2 = reduce(lambda x, y: x + y, tuple1, 50)  # 3+4+6+1+2+3+50依次相加在加一个50
result3 = reduce(lambda x, y: x - y, tuple1, 10)
print(result, result2, result3)

# map    源码  map(func, *iterables) --> map object   func:函数  *iterables迭代器
# 对列表中的奇数进行加100操作
result = map(lambda x: x if x % 2 == 0 else x + 100, list1)
# 遍历list1的元素,如果元素除以2等于0的话就返回本身,否则就加100,
print(list(result))

4.2、函数进阶

4.2.1 概念[特殊用法]

  1. 变量可以指向函数

    x = abs(-35)
    print(x)  # 35
    
    # 一个普通的变量可以指向一个函数,该变量就可以被当做函数调用
    f = abs  # abc返回参数的绝对值
    print(f)
    print(f(-100))
    
    
    def check():
        print("check")
    
    
    check()
    f1 = check  # 这里的f1就被当成函数调用 !!!赋值的时候不要加括号 
    f1()
    
  2. 函数也可以作为变量名

    函数就没有了原来的功能

    # 本质:函数名就是一个指向函数的变量
     print(abs(-28))
     # abs = "hello"
     # print(abs(-7)) #TypeError: 'str' object is not callable
    
    
  3. 函数作为参数使用

    把函数作为参数传入函数中不需要加括号

       # 调用形参中的函数,必须和原函数保持一致【注意是否需要传递参数】
     def test(a, b, fun):
         return fun(a) + fun(b)  # abs(43) + abs(-27)
    
    
     print(test(43, -27, abs))  # fun = abs
    
    
     def test1(s1, s2, func):
         return func(s1) + func(s2)
    
    
     print(test1("hello", "word", len))  # func=len  函数做为参数都不要打括号
    

4.2.2 闭包

在函数内部定义了另一个函数,即存在外部函数和内部函数

条件:

1.外部函数中定义了内部函数2.外部函数一定要有返回值,返回的值是:内部函数名3.内部函数引用了外部函数的变量
def func(a, b):  # 101 99
    c = 100

    def inner_func():  # 1.外部函数中定义了内部函数
        D = 20
        print("闭包", a + b + c + D)  # 3.内部函数引用了外部函数的变量

    return inner_func  # 2.外部函数一定要有返回值,返回的值是:内部函数名(不要加括号)


x = func(101, 99)  # 接收的是内部函数的地址
x()  # 调用内部函数
闭包的优点:在外部函数中定义的变量,在内部函数可以直接访问

闭包有什么缺点呢?
	闭包的缺点1,作用域没有那么直观
	闭包的缺点2,因为变量不会被垃圾回收所以有一定的内存占用问

闭包的作用:

  1. 可以使用同级的作用域

  2. 读取其他元素的内部变量

  3. 延长作用域

闭包总结:

  1. 闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成。

  2. 由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有用时释放,消耗内存

  3. 闭包的好处,使代码变得简洁,便于阅读代码。

  4. 闭包是理解装饰器的基础

4.2.3 装饰器

装饰器【Decorator】:使其他函数在不需要做任何代码的变动下,为函数添加功能,装饰器的返回值也是一个函数。

本质:装饰器的本质就是一个闭包,其作用是将一个函数作为参数,返回另一个函数

  • 装饰器函数运行在函数定义的时候

  • 装饰器需要返回一个可执行的对象

  • 装饰器返回的可执行对象要兼容函数f的参数

装饰器条件:

1.外部函数中定义了内部函数
2.外部函数一定要有返回值,返回的值是:内部函数名
3.内部函数引用了外部函数的变量
4.函数作为外层函数参数

使用装饰器

 @装饰器名字   
 def 函数名():     
     pass
  1. 简单的装饰器

    在闭包的前提下引用函数作为外层函数的参数

       import time
    
    
     # 装饰器带参数
     def zhuang1(func):  # 接收f1函数作为参数
         def wrapper(*args, **kwargs):  # 这里就要加*args,**kwargs 1.外部函数中定义了内部函数
             print("正在校验中.....")
             time.sleep(2)
             print("校验完毕.....")
    
             func(*args, **kwargs)  # 调用原函数f1	4.函数作为外层函数参数
    
         return wrapper  # 把装饰器在返回给f1 2.外部函数一定要有返回值,返回的值是:内部函数名
    
    
     @zhuang1
     def f1():
         print("我是户主one------one")
    
     f1()
    
  2. 带有参数的函数

import time


# 装饰器带参数
def zhuang1(func):  # 接收f1
    def wrapper(*args, **kwargs):  # 这里就要加*args,**kwargs
        print("正在校验中.....")
        time.sleep(2)
        print("校验完毕.....")
        # 调用原函数
        func(*args, **kwargs)

    return wrapper  # 把装饰器在返回给f1


sum = 10000


@zhuang1
def f1(sum):
    print("我是户主one------one", sum)


f1(sum)


@zhuang1
def f2(sum, name="abc"):
    print("我是户主{}-----two".format(name), sum)


f2(sum)


@zhuang1
def f2(sum, name="aaaaaa"):
    print("我是户主{}-----two".format(name), sum)


f2(sum, name="adfafasdasd")  # 覆盖了name的值
  1. 装饰的函数有返回值

    一定要返回原函数

# 如果装饰的函数有返回值需要在装饰器的内部函数中用return返回 装饰函数的运行结果
def say(func):
    def inner():
        print("-----2-----")
        return func()	# 这里一定要返回原函数

    return inner


@say
def show():
    return "-----1-----"


print(show())
  1. 使用多个装饰器装饰同一个函数
'''
如果装饰器是多层的,谁距离函数最近就优先使用哪个装饰器
'''
def zhuangfeng1(func):  # 定义装饰器1
    print("-----one---start")
    def warper():
        print("刷漆..")
        return func()  # 返回原函数

    print("end")
    return warper

def zhuangfeng2(func):  # 定义装饰器2
    print("-----two---start")
    def warper():
        print("打麻椒..")
        return func()  # 返回原函数

    print("end")
    return warper


# 使用装饰器
@zhuangfeng2
@zhuangfeng1
def fz1():
    return "户主一"


print(fz1())

"""
-----one---start
end
-----two---start
end
打麻椒..
刷漆..
户主一
"""
#总结:多个装饰器作用于同一个函数的时候,从上往下依次执行,但是,原函数只被调用一次
  1. 装饰器带有参数

    有三层

'''
    装饰器带参数
  带参数的装饰器是三层的
  	第一层: 负责接收装饰器的参数
  	第二层: 负责接收函数
  	第三层: 去接收函数里面的参数
'''
def outer(a):  # 第一层: 负责接收装饰器的参数
    def decorate(func):  # 第二层: 负责接收函数
        def wraper(*args, **kwargs):  # 第三层: 去买个接收函数的参数
            func(*args, **kwargs)
            print("--->铺地砖{}块".format(a))

        return wraper

    return decorate


@outer(100)  # 装饰器带有参数
def house(time):
    print("我{}日期拿到了毛坯房".format(time))


house("2020-9-9")

4.2.4 偏函数

通过设定默认参数,可以降低调用的难度,偏函数也可以起到这样的作用

概念:对函数的参数做一些控制的函数

注意:偏函数一般不需要自己定义,直接使用[functools模块其中提供了偏函数的使用]

import functools


def int2(x, base=2):
    return int(x, base)  


print(int2("1011"))
print(int2("1010", 8))

# 系统的functools模块中提供了偏函数的实现
# 参数:已经存在的函数名   默认参数
int3 = functools.partial(int, base=2)
print(int3("1110"))
print(int3("1110", base=10))

# 思想:根据一个已经存在的函数,通过修改该函数参数的默认值,生成一个新的函数,被称为偏函数

4.3、高阶函数

4.3.1 filter() 过滤

filter(function,iterable) :function:判断的条件 ,iterable可迭代对象

  • 必须要返回True和False,因为filter只判断True和False看保留与否
"""
filter(function,iterable)  通过一定的条件过滤可迭代对象中的元素

工作原理:把传入的函数依次作用于可迭代对象的每一个元素,根据返回的布尔值【True或者False】决定是否保留元素
如果返回True,则表示需要保留该元素;如果返回False,则表示需要过滤掉该元素
"""
# 将列表中的偶数筛选出来
list1 = [1, 2, 3, 4, 5, 6, 7, 8]

def func(num):
    # 保留偶数元素
    if num % 2 == 0:
        return True
    # 过滤奇数元素
    return False

# 依次遍历list1列表中的值,并放到func函数中判断,如果返回的为True就保留下来,并放入列表。为False就舍弃
newList4 = list(filter(func, list1))
print(newList4)


# 将爱好为"无"的数据剔除掉
data = [['姓名', '爱好', '年龄'], ['tom', '无', 10], ['jack', '唱歌', 28]]

def func2(s):
    if s == "无":
        return False
    return True

for line in data:
    result = list(filter(func2, line))
    print(result)

4.3.2 map() 映射

map(function,Iterable); function对象每个迭代对象进行处理,iterable:可迭代对象

  • function对象中一定要有返回值
"""
map(function,Iterable)  会根据的函数对指定的序列做出映射
function:函数
iterable:可迭代对象,序列
工作原理:函数会以序列中的每一个元素作为参数,返回包含函数的功能的新列表
功能:将传入的函数依次作用于序列中的每一个元素,并把结果作为新的iterable返回
"""

"""
传给map的函数的要求:
 a.参数只能有一个【默认将可迭代对象的一个元素传递给该函数】
 b.该函"
# 计算列表中各个元素的平方
list1 = [1, 2, 3, 4, 5]

# 方式一:
# map[def定义的函数]
def func(x):
    return x ** 2  # 必须要有返回值

result = map(func, list1)  # 将列表list1中的元素每个元素迭代,经过函数func的处理返回值
print(result)  # map对象 <map object at 0x02C770E8
newList4 = list(result)
print(newList4)


# 方式二:map[匿名函数]
newList5 = list(map(lambda x: x ** 2, list1))
print(newList5)

4.3.3 reduce() 迭代[累积]

reduce(function,iterable) function:需要进行的处理 iterable可迭代对象

  • function对象中一定要有返回值
  • 函数里面必须要有两个参数
"""
导入模块:functools
reduce(function,iterable):函数会对序列中的元素进行累积
功能:用传给reduce的函数先序列中的第1,2个元素进行操作,
用得到的结果和第3个元素进行操作,用得到的结果和第4个元素进行操作。。。。

举例:
f,[a,b,c,d]
reduce(f,[a,b,c,d])
工作原理:f(f(f(a,b),c),d),类似于递归 其实就是[1,2,3,4] (((1+2)+3)+4)
"""

"""
函数的注意事项:
 a.必须有两个参数
 b.必须设置返回值【报错:TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'】
"""
from functools import reduce
# 计算一个整数列表中元素的和

# 一:reduce【def定义函数】
def mySum(x, y):
    return x + y  # 函数必须设置返回值

result0 = reduce(mySum, list1)
print(result0)
"""
mySum(1,2)---->3
mySum(3,3)---->6
mySum(6,4)---->10
mySum(10,5)---->15
"""

# 二:reduce[匿名函数]
result1 = reduce(lambda x, y: x + y, list1)
print(result1)

面试题

from functools import reduce

# 将[1,3,5,7,9]转换为整数13579
list1 = [1, 3, 5, 7, 9]


def func(num, y): # 函数里面必须要有两个参数
    return (num * 10) + y # 必须要有返回值


new = reduce(func, list1)
print(new)
from functools import reduce

# 自定义一个函数,实现str转换为int的函数  int(xx)
def charToNum(s):
    # 自定义字符串和整型之间的映射关系
    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

    return digits[s]  # 返回字典对应的key


def func(num, y):  # 函数里面必须要有两个参数
    return (num * 10) + y


# 需求:“24356”------》24356
# a.产生映射关系
r0 = list(map(charToNum, "243567890"))  # 把返回的值添加到列表中
print(r0)
# b.使用reduce累积
r1 = reduce(func, r0)
print(r1)
print(type(r1))

五、Python的面向对象

5.1 面向对象的基本用法

5.1.1 概念

面向过程:具体化,流程化,解决一个问题,需要一步一步的分析,一步一步的实现.

面向对象:模型化,将现实中的事物抽象为程序中的属性或者功能,并且自己无需管理模型中方法实现的过程,只需要会调用.

面向对象的底层其实还是面向过程

面向对象的三大特征:1.封装性 2.继承性 3.多态性

优缺点:

面向过程:

  • 优点:性能比面向对象好,因为类调用时需要实例化,内存开销较大
  • 缺点:不易维护,不易复用,扩展

面向对象:

  • 优点:易维护,易复用,易扩展
  • 缺点:内存开销大,性能比面向过程差

:类是对象的抽象,将现实生活中的具体的事物中的属性或者行为抽象出来,作为程序中的方法或者属性,类是抽象的

对象:对象是类的实例,通过类创建现实生活中的一个具体的实物

5.1.2 定义一个类

一个类是由属性方法组成的

1. 属性: 普通属性,私有属性__
#定义类和属性
class Student:
    name = "李小小"  #属性
    age = 20
    __phone = 123  # 私有属性

You = Student()
print(You.name)
print(You.age)

You.age = 18
print(You.age)

You.set = "女"    #自己后面创建的
print(You.set)
2. 方法: 普通方法 类方法 静态方法 魔术方法
  • 普通方法

    定义:第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法);

    调用:只能由实例对象调用。

    格式:

    def 函数名():	
        pass
    
class Phone:
    brand = "小米"  # 属性
    price = 1999
    type = "mate 90"

    # Phone类里面普通方法:call
    def call(self):
        print("self---->", self)  # 指的当前类对象
        print("我的手机是:{},{},{}".format(self.brand, self.price, self.type))  # 通过self来访问类中的属性
        print("正在访问通讯录:")
        for i in self.addbook:  # 访问外部的属性
            print(i)
        print("正在打电话")
        print("留言:", self.note)  # 访问外部的属性


p1 = Phone()
p1.note = "我是phone1的note"  # 添加的属性
p1.addbook = [{1001: "张三"}, {1002: "李四"}]
p1.call()
  • 类方法

    定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);

    调用:实例对象和类对象都可以调用。

  """
类方法
 特点:
 	1.定义需要依赖装饰器@classmethod
 	2.类方法中参数不是一个对象,而是类 cls
 	3.类方法中只可以使用类属性
 	4.类方法中可以使用类中的普通方法,不过要先创建实例调用 cls().show()
 	
类方法的作用:
因为只能访问类属性和类方法,所以可以在对象创建之前,如果需要完成一些动作(功能)

修改传参
 def show1(cls, b="abc"): # 方法一,在类方法中定义
        cls("类方法-->").eat()  # 方法二,修改普通方法的传参
可用类方法修改私有属性:
	 cls.__age = 200  # 用类方法修改私有属性
"""
class Dog:
    a = "abc"  # 类属性

    def __init__(self, name):  # 初始化方法
        self.name = name

    def run(self):
        print("{}在跑".format(self.name))

    def eat(self):  # 普通方法的参数为self
        print("{}在吃东西".format(self.name))
        self.run()  # 调用类中的普通方法

    @classmethod
    def show(cls, a="dfg"):  # 类方法,需要定义@classmethod,类方法的参数为cls
        print("我是类方法{}".format(a))
        print(cls)  # <class '__main__.Dog'>

    @classmethod  # 只能访问类属性和类方法 调用类方法是用cls.类方法名()
    def show1(cls, b="abc"):
        print("我是类方法2{}".format(b))
        cls.show()  # 在类方法中调用类方法
        cls("类方法-->").eat()  # 修改普通方法的传参


c = Dog("小花")
c.run()  # 普通方法
c.eat()
print(c.a)  # 获取类中的属性
c.show1()
"""
小花在跑
小花在吃东西
小花在跑
abc
我是类方法2abc
我是类方法dfg
<class '__main__.Dog'>
类方法--》在吃东西
类方法--》在跑
"""```

```python
class Person:
    __age = 18

    def show(self):
        print("--->", Person.__age)

    @classmethod
    def update_age(cls):
        cls.__age = 200  # 用类方法修改私有属性
        print("我是修改的类方法")

    @classmethod
    def show_age(cls):
        print("我是修改后的类方法", cls.__age)
        cls().show()


a = Person()
a.show()
a.update_age()
a.show_age()
"""
---> 18
我是修改的类方法
我是修改后的类方法 200
---> 200
"""
  • 静态方法
静态方法:很类似于类方法
    1.需要装饰器@staticmethod
    2.静态方法是需要传递参数(cls,self)
    3.也只能访问类的属性和方法,对象的是无法访问的
    4.加载时机同类方法
静态方法可以调用没有self的普通方法,可以调用类方法
class Person:
    __age = 18

    def show(self):
        print("--->", Person.__age)

    def show1():
        print("123456676767867")

    @classmethod
    def update_age(cls):
        cls.__age = 200  # 用类方法修改私有属性
        print("我是修改的类方法")

    @classmethod
    def show_age(cls):
        cls.update_age()
        print("我是修改后的类方法", cls.__age)

    @staticmethod
    def test():
        print("我是静态方法")
        Person.show_age()  # 静态方法可以调用类方法
        # Person.show() #静态方法不能调用里面有self的普通方法
        Person.show1()  # 里面没有self的就可以调用
        s = Person.__age
        print("我是调用的类属性", s)


a = Person()
a.test()
"""
我是静态方法
我是修改的类方法
我是修改后的类方法 200
123456676767867
我是调用的类属性 200
"""
  • 魔术方法

    """
    魔术方法
        魔术方法就是一个类/对象中的方法,和普通方法唯一的不同是,普通方法需要调用!而魔术方法是在特定时刻自动触发
        1.__init__: 初始化魔术方法
            触发时机:初始化对象时触发(不是实例化,但是和实例化在一个操作中)
        2.__new__: 实例化的魔术方法
            触发时机:在实例化对时触发,不用调用先执行,在init之前执行
        3.__call__:对象调用方法
            触发时机:将对象当成函数使用的时候,会默认调用此函数中内容
        4.__del__ : del p1  删除地址的引用,当一块空间没有了任何引用,默认执行del(其实就是当什么语句函数执行完后就执行)
    			
        5.__str__:
            触发时机:打印对象名, 自动触发去调用 __str__里面的内容
            注意:一定要在__str__方法中添加return,return后面的内容就是打印对象看到的内容
    """
    
class Person:
    def __init__(self, name):  # 初始化对象
        print("---->init", self)
        self.name = name

    def __new__(cls, *args, **kwargs):  # 执行函数的时候,先执行new,在init()之前
        # new不用调用,先执行
        print("--->new")
        nc_new = object.__new__(cls)
        print("我是new魔法方法的内存地址", nc_new)
        return nc_new

    def __call__(self, name):  # 将对象当成函数使用的时候,会默认调用此函数中的内容
        print("把对象当前函数用 --->call")
        print("执行对象得到参数是:", name)


p = Person("aa")  #
p("x")  # 将对象当成函数使用的时候,会默认调用此函数中的内容
'''
del 
    1.对象赋值
        p = Person1()
        p1 = p
        说明:p和p1共同指向同一个地址
    2.删除地址的引用
        del p1 删除p1对Person1的地址引用
    3.查看对地址的引用次数
        import sys
        sys.getrefcount(p)
    4.当一块空间没有了任何引用,默认执行del
'''
import sys


class Person1:
    def __init__(self, name):
        self.name = name

    def __del__(self):
        print("我是del方法")


p = Person1("CCTV")
p1 = p
p2 = p
print(sys.getrefcount(p))
del p1  # 删除地址的引用,当一块空间没有了任何引用,默认执行del
print(sys.getrefcount(p))
# __str__
class Person2:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):  # 和java中的重写toString方法一样
        return "姓名:" + self.name + "年龄:" + str(self.age)


p = Person2("tom", 20)
print(p)
# 单纯打印对象名称,出来的是一个地址
# 如果想在打印对象名的时候能够给开发者更多一些信息量,就重写str方法
总结
静态方法只能访问没有slef的普通方法,静态方法通过类名访问
类方法通过cls访问
类方法  静态方法
不同:
    1.装饰器不同
    2.类方法是有参数的(cls),静态方法没有参数
相同:
    1.只能访问类的属性和方法,对象的是无法访问的
    2.都可以通过类名调用访问
    3.都可以在创建对象之前使用,因为是不依赖于对象

普通方法 与 两者区别:
不同:
    1.没有装饰器
    2.普通方法永远是要依赖对象,因为每一个普通方法都有一个self
    3.只有创建了对象才可以调用普通方法,否则无法调用。

5.2 面向对象的三大特性

面向对象的三大特性:封装,继承,多态

5.2.1 封装

封装:

  • 私有化属性
  • 定义公有set和get方法

格式: __属性(私有属性):就是将属性私有化,访问范围仅仅限于类中

好处:

  • 隐藏属性不被外界随意修改
  • 如果要修改的话就要修改set,get方法

封装实例 就是将属性私有化

"""
  __xxx = 1  # 私有属性  
  def setXXX(self,name): #set是加判断
        if  name是否符合条件
            赋值
        else:
            不赋值
    def getXXX(self):  #通过get获取值
        return self.__xxx
"""
class Student:
    def __init__(self, name, age):
        self.__name = name  # 私有属性
        self.__age = age
        self.__scort = 99

    # set 和 get方法
    def setName(self, name):  # 用set来设置,name的长度,小于等于6位的话就可以赋值
        if len(name) <= 6:
            self.__name = name
        else:
            print("名字输入有误")

    def getName(self):
        return self.__name

    def __str__(self):  #
        return "姓名:{},年龄:{},成绩:{}".format(self.__name, self.__age, self.__scort)


p = Student("李小", 20)
p.setName("ffff")  # set是用来修改
print(p.getName())  # get是用来获取
print(p)
1. 访问私有属性的方法

方法一: set 和 get 方法

class Student:
    def __init__(self, name):
        self.__name = name

    def setName(self, name):  # 用set来设置,name的长度,小于等于6位的话就可以赋值
        if len(name) <= 6:
            self.__name = name
        else:
            print("名字输入有误")

    def getName(self):
        return self.__name


p = Student("李小")
p.setName("ffff")  # set是用来修改
print(p.getName())  # 获取私有属性

方法二:使用装饰器的方式

@property  #其实就是get
def a():
    pass
@a.setter  #就是set
def b():
    pass
class Student:
    def __init__(self, name, age):
        self.name = name
        self.__age = age

    # 先有get的,在用set的
    @property
    def age(self):  # 其实就是get
        return self.__age

    @age.setter
    def age(self, age):  # 其实就是set
        if age > 0 and age < 100:
            self.__age = age
        else:
            print("输入有误")

    def __str__(self):
        return "姓名:{},年龄:{}".format(self.name, self.__age)


s = Student("李小小", 20)
print(s.age)  # 获取私有属性
print(s)

方法三:通过_类__私有属性名

class Stu(object):
    def __init__(self):
        self.__age = 99


s = Stu()

print(s._Stu__age)  # 通过_类名__私有属性名 访问

# 查看创建对象里面的属性
print(dir(s))  # 查看里面的属性
print(s.__dir__())  # 查看里面的属性 和dir(p)一样
总结

总结:封装就是将属性私有化,

方法一:定义set,get方法。

方法二:使用装饰器@property(get),@方法名.setter(set)

方法三:可以用_类名__私有属性名 ,访问类中的私有属

用dir(),或者对象名.dir() ,查看里面的属性

5.2.2 继承

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制.

通过继承创建的新类称为子类派生类,被继承的类称为基类父类超类。python没有重载, 只有重写...........

单继承语法
class 派生类名(基类名)
    ...

在python中子类继承的一些特点:

  • 特点: 1.如果类中不定义__init__,就默认调用父类super class的__init__ 2.如果类继承父类,也需要定义自己的__init__,就需要在当前类的__init__调用一下父类的_init__:可以用自己的init 3.如何调用父类__init__: super().init(参数) super(类名,对象).init(参数) 4.override:重写,如果子类重写了父类的方式,就调用 子类的方法 5.子类中可以调用父类方法 super().方法名(参数)

    6.私有属性,只能在自己的类中访问。但是可以调用父类中使用私有属性的方法。方法中的属性是父类自己的私有属性

class Person:  # 父类,基类
    def __init__(self, name, age):
        self.name = name  # 王一
        self.age = age  # 30
        self.__phone = "123"

    def run(self):
        print("{}跑步中....{}".format(self.name, self.__phone))

    def eat(self):
        print("{}吃饭中....".format(self.name))


class Student(Person):  # 继承了父类,就表示获取了它所有的方法和属性(私有不算)
    def __init__(self, name, age, sex):  # 差不多是重写了构造方法
        super().__init__(name, age)  # 使用了父类定义的属性,继承了父类的属性
        self.age = 99
        self.sex = sex

    # 这样出现的是警告,波浪线才是错误
    def eat(self, food):  # 重写了父类的eat方法,python只有重载,没有重写
        super().run()  # 调用父类的eat方法
        print("{}正在吃{},{},{}".format(self.name, food, self.sex, self.age))


Person("王一", 30)

stu = Student("李小小", 20, "男")
stu.eat("屎")

print("-------------------" * 2)
# 验证子类能不能访问父类的私有属性
# 1.自己的类中只能用自己类中的私有属性
class Person1:
    def __init__(self, sex):
        self.sex = sex
        self.__money = 200
        self.name = "abc"

    def show1(self):
        print("我是父类的show{},{}".format(self.__money, self.name))


class Student2(Person1):
    def __init__(self, sex):
        super(Student2, self).__init__(sex)  # 使用super(),访问父类中的普通属性
        self.__money2 = 500  # 私有属性,只可以在本类中访问
        self.name = "deg"
        super().show1()  # 使用super().show1()  访问父类中使用私有属性的方法

    def show(self):
        print(self.__money2, "------", self.sex)  # 访问自己类中的私有属性,不能访问父类的私有属性


class Stu3(Person1):
    def __init__(self, sex):
        super(Stu3, self).__init__(sex)

    def show(self):
        print(self.name, "--=-", self.sex)  # 访问父类的普通属性


s = Student2("男")
s.show()

s3 = Stu3("女人")
s3.show()
"""
我是父类的show200,deg
500 ------ 男
abc --=- 女人
"""

如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。

多继承语法:

派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:

class SubClassName (ParentClass1[, ParentClass2, ...]):    ...
'''
python多继承
    A(B,C)  逗号隔开

经典类class A,  新式类:class A(oobject)
但python3中取消了经典类,默认继承object,即python3中class A也是新式类。
'''


# 深度优先:当调用A类test函数时,如下图所示,先查找A类中有无test函数,再查找B类,再查找D类,最后查找C类。 python2
# A-->B-->D-->C
# 广度优先:当调用A类test函数时,如下图所示,先查找A类有无test函数,再查找B类,再查找C类,最后查找D类。 python3
# A-->B-->C-->D
class D:  # 默认继承了object
    def test(self):
        print("D")


class C(D):
    def test(self):
        print("C")


class B(D):
    pass
    # def test(self):
    #     print("B")


class A(B, C):
    pass
    # def test(self):
    #     print("A")


a = A()
a.test()

# 查看执行顺序
print(A.__mro__)

import inspect

print(inspect.getmro(A))
总结

总结:继承就是子类继承父类

  1. has a :使用自定义的类型,系统类型

  2. Is a :子类(父类) ,子类不能继承父类的私有属性,私有属性,可能在自己的类中使用

  3. 多继承 :广度优先(python3) ,深度优先(python2)

查看执行顺序:对象名.mro 或者import inspect print(inspect.getmro(对象名))

5.2.3 多态

python中多态的作用:让具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容(功能)的函数

继承是多态实现的前提

多态的好处:提高代码的复用性

Python中多态的特点

 1、只关心对象的实例方法是否同名,不关心对象所属的类型;
 2、对象所属的类之间,继承关系可有可无;
 3、多态的好处可以增加代码的外部调用灵活度,让代码更加通用,兼容性比较强;
 4、多态是调用方法的技巧,不会影响到类的内部设计。

多态性:

 Java中多态性的表现: 多态性,可以理解为一个事物的多种形态。同样python中也支持多态,但是是有限的的支持多态性, 主要是因为python中变量的使用不用声明,所以不存在父类引用指向子类对象的多态体现,同时python不支持重载。 在python中 多态的使用不如Java中那么明显,所以python中刻意谈到多态的意义不是特别大
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class Cat(Animal): #动物的形态之一:猫
    def talk(self):
        print('say miaomiao')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')

c = Cat()
d = Dog()
p = Pig()

def func(obj):
    obj.talk()

func(c)
func(d)
func(p)
"""
say miaomiao
say wangwang
say aoao
"""

其实就是调用相同的方法得到不同的结果

总结
总结:python没有严格意义上的多态,因为它没有java里面的访问修改符,所以都可以访问,但我们可以用isintance()判断 该对象是不是类中的,是类中的就让它通过。

5.2.4 单例模式

单例模式:是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.

当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

其实就是一个类里,只想有一个实例化对象。不然多个会导致严重浪费内存资源
class Person:  # 其实就是先创建一个对象空间,并保存,下次在创建的时候就使用上一次创建的对象空间
    __instance = None

    # 重写__new__方法
    def __new__(cls):  # 在init前执行
        if cls.__instance is None:
            cls.__instance = object.__new__(cls)  # 把第一次创建的地址给__instance

            return cls.__instance
        else:
            return cls.__instance


# 执行过程:就是先创建一个对象空间p,然后进入new方法中,把开辟的地址p,给cls.__instance,并返回了
# 第二次创建的p2,进入new后__instance它不是为空的,所以就直接else 返回第一次创建的地址。这样就形成了 永远就用同一个地址
p = Person()
p1 = Person()
print(p, p1)