Python基础知识整理

本基础知识整理参考,廖雪峰老师的Python3教程整理,点击后面的链接访问老师的教程

廖雪峰老师的Python3教程

如果发现错误之处,请及时联系本人。感谢~ 邮箱:[email protected]

数据类型和变量

变量

变量由变量名,赋值符号,变量值组成,例如: n = 1
在Python中数据又称为对象,每创建一个对象都会创建对象的id,type,值
使用id()查看变量的内存地址,使用type查看变量的类型

身份比较:
# 身份比较比较的是id值
>>> x = 300
>>> id(x)
>>> y = 300
>>> id(y)
>>> x is y
False
>>>
值比较:
# 值比较的是两个变量的值是否相同
>>> x = 300
>>> y = 300
>>> id(x)
54002288
>>> id(y)
54002352
>>> x == y
True
>>>
类型比较
# 类型比较的是两个变量的类型
>>> x = 300
>>> y = 300
>>> type(x)

>>> type(y)

>>> type(x) is type(y)
True
>>>
变量赋值操作补充
# x = 1   # 就是一个赋值操作 没有任何返回值
# 链式赋值:
#   a=b=c=10
# 多元赋值(变量值交换):
#   x,y=y,x
>>> x = 1
>>> y = 2
>>> x,y = y,x
>>> x
2
>>> y
1
>>>
# 解压序列类型:
# s = 'hello'
# a,b,c,d,e=s
# 只打印出 对应的  a 和 e的值,使用'_'来填充中间的变量, '_'约定俗称代表丢弃的变量
# a,_,_,_,e = s
# a,*_,e    # 使用*_ 代表中间全部

数据类型分类

可变或者不可变的定义:在id和type不变的情况下,值可变的数据是可变类型.id,type,value都不可变的是不可变类型

  • 可变和不可变
    • 可变:列表,字典,集合
    • 不可变:数字,字符串,元组
  • 存放数值的个数
    • 单个值:数字,字符串
    • 多个值:列表,元组,字典,集合
  • 取值方式
    • 直接取值: 数字
    • 下标取值: 字符串,列表,元组
    • 按key取值: 字典
    • 特殊的取值方式: 集合

整数

Python可以处理任意大小的整数,包括负整数,在程序中表示的方式和数学上的是一样的。
由于计算机使用的是二进制,有时候使用十六进制表示整数比较方便,十六进制使用0x 为前缀和0-9a-z 表示。例如:0xff00

浮点数

浮点数就是小数,在Python中整数运算永远是精确的,但是浮点数则可能出现四舍五入的情况。

字符串

字符串在Python中以单引号或者双引号引起来的字符,例如: '1''Python语言'"中文"。如果,字符串含有'或者" 则可以使用\来进行转义。例如:'I\'m \"OK\"!'。如果转义很多可以使用r'字符串',被r包括的字符串默认不会转义

字符编码

Unicode编码可以转化为任意的编码,计算机内存统一使用的就是Unicode编码
如果想把UTF8编码的字符转成GBK编码的字符,则需要UTF8--->Unicode--->GBK这个顺序转
硬盘上存储的都是字节
Python3版本中,字符都是以Unicode编码的,也就是说Python3是支持多语言的.
Python3内置函数ord()chr()分别可以把字符转为ASCII码和转回字符串
转换编码使用encodedecode,前者编码,后者解码
Python文件中开头的两行分别是指定Python解释器,这个py文件是以什么编码方式读取

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

Python字符串占位符

如果不确定使用什么占位符,那么可以使用使用%s代替,换句话说%s有点像万能的占位符

占位符 替换内容
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数

list

Python内置的一种列表类型的数据类型,列表是有序的并且是可变的,可以随时删除或者添加其中的数据
特性: 列表是有序的,列表中的元素可以是任意类型的数据

常用列表操作

取值: 通过索引取值

>>> l = [1,2,3]
>>> l[0]
1
>>> l[2]
3
>>>

切片: 通过索引范围进行切片

l = [1, 2, 3, 4, 5, 67, 7, 8]
print(l[:1])  # [1]
print(l[1:4])  # [2, 3, 4]

添加值: append从列表右边添加

l = [1, 2, 3]
print(l.append(4))  # None
print(l)  # [1, 2, 3, 4]

插入值: insert 需要传入插入的位置

l = [1, 2, 3]
print(l.insert(0,7))  # None
print(l)  # [7, 1, 2, 3]

删除值: pop默认删除右边最后一个值,可以通过传入索引来删除某个值

l = [1, 2, 3]
print(l.pop())  # 3 返回被删除的值
print(l)  # [1, 2]
l.pop(1)
print(l)  # [1]

获取索引值: index传入字符串,获取该字符串在列表中的第一个索引位置

l = [1, 2, 3, 1]
print(l.index(1))  # 0 列表中两个 1 只返回找到的第一个值得索引

扩展列表: extend 无法扩展元组元素或者字典元素

l = [1, 2, 3]
l2 = ['中', '文'] 
print(l.extend(l2))  # None
print(l)  # [1, 2, 3, '中', '文']

移除元素: remove 通过传入元素的名来移除对应的元素,这里跟pop区分,pop接收的是索引

l = [1, 2, 3,'中', '文']
print(l.remove('中'))   # None
print(l)  # [1, 2, 3, '文']

tuple

特性:元组内的所有元素不能更改,元素可以是任意数据类型,元组也是有序的
元组中可以包含list,元组中的list中的元素是可以更改的.当然list中也可以有元组.

元组常用操作

元组操作基本同list是一致的,但是没有任何修改的方法,例如:insert, pop, remove, extend等修改数据的方法

dict

Python内置的字典数据类型,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。
字典的key必须是不可变类型,或者说必须是可hash类型.value的值是可以改变的,可以是任意类型的数据.字典的取值是无序的.无法通过索引取值

字典常用操作

取值: 通过字典的key取值, 如果key不存在会抛出错误

d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d[1])  # a
print(d['b'])  # 2
print(d[5])  # {'a': 1}

不报错取值方式: get取值如果key不存在不会报错,会返回None

d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d.get(77))  # None
print(d.get(4))  # (7, 6, 2)

循环: 普通的方式循环字典,只会取出字典的key

d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
for x in d:
    print(x)  # 1  b  3  4  5

以元组形式显示: items() for 循环时取出的是元组形式的数值

d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
for x in d.items():
    print(x)
# (1, 'a')
# ('b', 2)
# (3, [1, 2, 3])
# (4, (7, 6, 2))
# (5, {'a': 1})

指定取key或者value: .keys.values

d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d.keys())   # dict_keys([1, 'b', 3, 4, 5])
print(d.values())  # dict_values(['a', 2, [1, 2, 3], (7, 6, 2), {'a': 1}])

删除字典键值对: pop传入key删除对应的key和value

d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d.pop('b'))  # 2  返回删除的值
print(d)  # {1: 'a', 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}

随机删除一组数据: popitem()

d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d.popitem())  # (5, {'a': 1}) 
print(d)  # {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2)}

添加键值对: setdefault() 传入keyvalue如果key已存在则不尽心任何操作

d = {1: 'a', 'b': 2, 4: (7, 6, 2), 5: {'a': 1}}
print(d.setdefault('test',1))  # 1 返回新添加的 value
print(d)  # {1: 'a', 'b': 2, 4: (7, 6, 2), 5: {'a': 1}, 'test': 1}

更新keyvalue,如果key不存在会建立该键值对

d = {1: 'a', 'b': 2, 4: (7, 6, 2), 5: {'a': 1}}
d[1] = 'abc'
print(d)  # {1: 'abc', 'b': 2, 4: (7, 6, 2), 5: {'a': 1}}

字典格式化: .fromkeys()传入key和value,前面所有的key都等于同一个value

d={}.fromkeys(['name','age'],None)
print(d)  # {'name': None, 'age': None}

set

特性:集合内的元素必须是唯一的,集合内的元素必须是可hash的,也就是不可变类型,集合是无序的
集合运算:

python_s = {'egon', 'alex', 'abc', 'wang'}
linux_s = {'alex', 'abc', 'aaa', 'bbb'}

print(python_s.intersection(linux_s))  # 交集:python_s & linux_s
print(python_s.union(linux_s))  # 并集: python_s | linux_s
print(python_s.difference(linux_s))  # 差集: python_s-linux_s
print(python_s.symmetric_difference(linux_s))  # 对称差集: python_s ^ linux_s

更新: updata 如果单独传入字符串会被拆开,如下代码示例

s1={'a',1}
s2={'a','b',2,'中文'}

s1.update(s2)
print(s1)  #  {1, 2, '中文', 'b', 'a'}
s1.update('字符串')
print(s1)  # {'字', 1, 2, '串', '中文', 'b', 'a', '符'}

添加: add 添加一个元素给集合

s1 = {'a', 1}
print(s1.add('asd'))  # None
print(s1)  # {1, 'asd', 'a'}

删除元素:discard(),remove()前者删除不存在元素不会报错,后者会,都是传入元素名

s1 = {'a', 1}
print(s1.discard('a'))  # None
print(s1)  # {1}
print(s1.remove(1))  # None
print(s1) # set()

其他补充:

s.issubset() # 判断是否是子集
s.issuperset() # 判断是否是父集
s.isdisjoint() # 没有交集返回True

函数

函数返回值

  • 通常有参函数是需要返回值的。
  • 返回值的几种形式:
    1. 如果函数内没有return就返回None
    2. return 返回的数据类型没有限制,也可以返回多个值,以元组形式,括号可加可不加
  • return的效果:
    • 终止函数的执行,只能返回一次值

函数参数

Python是一种弱类型的语言,参数可以传入任何类型数据
形参与实参:可以理解为形参就是变量名,而实参就是变量值

def foo(x,y) # x和y是形参
  return x+y
# 执行
foo(1,2) # 1,2 是实参

位置参数:按照从左到右的顺序依次定义的参数叫位置参数。

def foo(x,y): # 按位置定义的形参,必须被传值,多一个少一个都不行。
  print(x)
  print(y)
foo(1,2) # 按位置定义的实参,会一一跟形参对应。

关键字参数:指的是实参在定义时,按照key=value形式定义。

def foo(x,y): #形参还是按位置定义的。
  print(x)
  print(y)
foo(y=1,x=2) #打破位置的限制,不需要跟形参一一对应,直接指定传值。但是还要遵循不能多不能少。
  • 注意的问题
    1. 位置实参可以和关键字实参混合使用,但是位置实参必须在关键字实参的前面。
    2. 位置实参可以和关键字实参混合使用,但是一个形参只能被传值一次不能被重复传值。

默认参数:定义函数阶段,就已经为形参赋值。

def foo(x, y=10): # 形参在定义的时候赋值。
  print(x)
  print(y)
foo(x=2) # 实参可以不输入对应的形参,如果输入了就不使用默认参数。
  • 注意的问题
    1. 必须放在位置参数后面。
    2. 默认参数通常要定义成不可变类型。
    3. 默认参数只在定义时被赋值一次。

形参的应用:值经常变化的需要定义成位置形参,值大多数情况下都一样需要定义默认参数。
实参的应用:怎么爽怎么用,但是要遵循相应的规则。
可变长参数:可变长值得是实参的个数不固定 *号等同位置参数

按位置定义的可变长实参:*
按关键字定义的可变长实参:**

def func(x,y,*args): # x=1 y=2, args=(3,4,5,6)元组形式
  print(x,y)  # 1 2
  print(args)  # (3, 4, 5, 6)
# 执行代码
func(1,2,3,4,5,6) # 实参为位置参数使用*args
func(1,2,*(3,4,5,6)) # 等同于 func(1,2,3,4,5,6)
def func(x, y, **kwargs): # x=1 y=2, kwargs={‘z’:6} 字典形式
  print(x, y)  # 2 4
  print(kwargs)  # {'z': 6}
# 执行代码
func(x=2, y=4, z=6) # 实参为关键字使用**kwargs

命名关键字参数:*后面定义的形参(*,name,age)必须以关键字形式被传值

def regist(*,name,age):
  print(name)
  print(age)
# 执行代码
regist(name='abc',age=11)

形参:位置参数,默认参数,*.args 命名关键字参数,**kwargs #一般定义形参就按照这种顺序

名称空间与作用域

命名空间:存放名字与值的绑定关系

  • 种类:
    • 内置名称空间
      • 内置在Python解释器内的自带的名字,解释器一启动就生成这个空间
    • 局部名称空间
      • 定义在函数内部的名字
      • 局部名称空间只有在调用函数的时候生效,执行完毕失效
    • 全局名称空间
      • 文件级别定义的名字都会存放于全局名称空间,执行python文件时会产生

加载顺序:内置=>全局=>局部
取值顺序:局部=>全局=>内置

# 全局变量示例
def func():
  pass
class foo:
  pass
import os
if 1 > 2:
  y = 3 # 这里y没有顶头写但也是全局变量
# 局部变量示例
max = 10
def tmp():
    max = 20
print(max)
tmp() # 会打印出20 因为局部优先于全局

作用域:作用范围

  • 种类:
    • 全局作用域
      • 内置名称空间和全局名称空间的名字,作用范围是全局范围。在整个文件的任意位置都能被引用,全局有效。
    • 局部作用域
      • 局部名称空间中的名字,作用范围是局部范围生效。只在函数内部可被引用,局部有效

作用域搜寻顺序: 局部作用域=>全局作用域

函数的作用域在定义的时候就已经固定死了。


补充:

搜寻变量时,只能由“内”看“外”。不可以反过来。例如在函数内部定义的变量,无法在全局被直接引用。
但是如果在函数内部引用一个变量,但是函数内没有定义,那么会去全局作用域搜寻变量。

x = 1
def func():
  global x # 声明接下来声明的变量x为全局变量
  nonlocal x # 声明接下来变量x为局部变量(只在函数内部寻找,找不到则会报错)
  x =2
  print(globals()) # 打印全局作用域name
  print(locals()) # 打印局部作用域name
# 执行代码
func()

函数对象:函数是第一类对象,指的是函数可以被当数据传递,可以被引用,可以当函数的返回值,可以当容器的元素

闭包

闭包就是在函数内部定义的函数,包含对外部作用域的引用,但不包含全局作用域。因为函数的作用域在定义的时候就固定死了,所以闭包函数有自带作用域和延迟计算的特点。

闭包函数定义:如果一个内部函数,包含了对外部作用域的引用,但是不是包含全局作用域。那么这个函数就被认为是闭包函数。闭包函数可以使用“.closure” 来查看闭包函数的属性。下面我们来看一个示例:

def t():
    money = 100
    def s():
        print(money)
    return s            # 返回函数s,不是函数执行的结果

c = t()
c()
print(c.__closure__)   #查看属性
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-7/day7_笔记.py
# (,)

# Process finished with exit code 0

函数s就是在函数t中定义的内部函数.
函数s引用了一个外部的变量money,但是不是全局变量。则函数s就是一个闭包函数。

装饰器

装饰器本质可以是任意可调用对象,被装饰的对象也可以是任意可调用对象。

  • 装饰器功能:

    • 在不修改被装饰对象源代码以及调用方式的前提下,为其添加新的功能。
  • 装饰器的语法:

    • 在被装饰对象的正上方的单独一行,@装饰器名字。会把正下方的函数名调用装饰器,处理完返回给函数名。
    • 多个装饰器:谁在上面谁先执行,谁在下面谁先计算
import time          # 导入模块
import random

def timmer(func):   # 装饰器模块
    def wrapper():    # 定义一个闭包函数wrapper
        stime = time.time()    # 闭包函数内 计算index的睡眠时间 ,这里是开始计算
        func()      # 执行index的函数内容
        stptime = time.time()    # index的函数执行完毕后的停止时间
        print('run time is %s'%(stptime-stime))    # 打印运行了多久
    return wrapper()

def index():  # 定义函数index
    time.sleep(random.randrange(1,2))  # 函数内执行睡一定时间,然后打印欢迎信息
    print('welecome to index page') 

index = timmer(index)    # 调用装饰器
# 运行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-7/tmp.py
# welecome to index page
# run time is 1.0000858306884766

# Process finished with exit code 0

上面这个是最简单的装饰器示例。如果我们还要传参,让用户感觉不出装饰器的怎么办?我们来看下面的。

import time
import random
# 装饰器
def timmer(func):
    def wrapper(*args,**kwargs):     # 接受可变长参数
        start_time = time.time()
        res=func(*args,**kwargs)        # 接收保存对应函数 的return 返回值
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res      # 执行完毕 return 函数
    return wrapper
# 被装饰函数
timmer              # 使用@方式调用装饰器
def index():
    time.sleep(random.randrange(1,5))
    print('welecome to index page')
@(知识梳理笔记)timmer
def home(name):           # 需求一个传入参数
    time.sleep(random.randrange(1,3))
    print('welecome to %s HOME page' %name)
    return 123123123123123123123123123123123123123123     #return 有返回值

index()      #调用 无参数传入
print(home('abc'))    # 调用传入参数‘abc’
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-7/tmp.py
# welecome to index page
# run time is 2.0000545978546143
# welecome to abc HOME page
# run time is 2.0000154972076416
# 123123123123123123123123123123123123123123

# Process finished with exit code 0
wraps模块

让函数保留原来的说明信息。
在闭包函数上面 @wraps 可以把原代码的解释,引用到装饰器中,让用户彻底无法感知装饰器的存在
使用 func.doc 和 print(help(func))来查看函数的注释信息

from functools import wraps
import time
def coutime(func):
    @wraps(func)           # 调用wraps模块
    def wrapper(*args,**kwargs):
        start = time.time()
        ret = func(*args,*kwargs)
        stop = time.time()
        print('run time is %s'%(stop - start))
        return ret
    return wrapper
@coutime
def timerand(name,age):
    'askdnalskdjlaksjdlasjdasd'
    time.sleep(random.randrange(1,5))
    print('Hello %s age:%s'%(name,age))

help(timerand)             # 使用help查看函数的注释说明
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-8/day8_笔记.py
# Help on function timerand in module __main__:   

# timerand(name, age)
#     askdnalskdjlaksjdlasjdasd          # 完全隐藏
    
# Process finished with exit code 0

有参装饰器

装饰器加括号表示执行,会执行外层函数,然后把函数对象传入内层接收
类似加壳(写代码的时候),脱壳(执行装饰器)的过程

def high(auth_path='file'):  # 接受参数
    def big(func):
        def up(*args, **kwargs):
            if auth_path == 'file':
                func('file')
            elif auth_path == 'ldap':
                func('ldap')
            elif auth_path == 'mysql':
                func('mysql')
        return up
    return big
    
@high(auth_path='ldap')  # 调用装饰器传入参数
def pr(*args):
    print('hello %s' % args)
# 执行代码
pr()  # hello ldap

迭代

点击这里>>>完全理解迭代对象,迭代器,生成器

重复执行
下一次的重复执行依赖于上一次执行的结果

以下代码只满足重复执行,每下一次执行都是从头开始执行的,所以不属于迭代。

while True:
    cmd=input('>>: ')
    print(cmd)

以下代码满足重复执行,而且每下一次执行都是从上一次执行后的结果开始执行的,属于迭代

l=['a','b','c','d']
count=0
while count < len(l):
    print(l[count])
    count+=1

迭代器

python为了提供一种不依赖于索引的迭代方式,会为一些对象内置__iter__方法,obj.__iter__称为可迭代的对象。

字符串、元组、列表、集合、字典、文件等都是可迭代对象,迭代的过程中都能够用到迭代器。

s1='hello'
l=[1,2,3]
t=(1,2,3)
set1={1,2,3}
d={'a':1,'b':2,'c':3}
f=open('db.txt',encoding='utf-8')

迭代器就是obj.__iter__()得到的一个结果,每次使用obj.__next__()都能够获得一个迭代后的结果,当所有的元素都迭代后,会抛出StopIteration的错误提示表示没有内容可以继续迭代了。

  • 迭代器的优缺点:
    • 迭代器的优点
      • 提供了一种不依赖于索引的取值方式
      • 惰性计算。节省内存
    • 迭代器的缺点:
      • 取值不如按照索引取值方便
      • 一次性的。只能往后走不能往前退
      • 无法获取长度

可迭代对象执行的结果就是迭代器。
判断是不是可迭代对象或者迭代器:

from collections import Iterable,Iterator       #导入模块
print(isinstance(s, Iterable))       #是否是迭代对象
print(isinstance(s, Iterator))       #是否是迭代器

生成器

生成器是一种特殊的迭代器。它只需要一个yiled关键字。 生成器一定是迭代器(反之不成立)

def fib():
    prev, curr = 0, 1
    while True:
        yield curr
        prev, curr = curr, curr + prev

>>> f = fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
  • 总结:
    • 容器是一系列元素的集合,str、list、set、dict、file、sockets对象都可以看作是容器,容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象。
    • 可迭代对象实现了iter方法,该方法返回一个迭代器对象。
    • 迭代器持有一个内部状态的字段,用于记录下次迭代返回值,它实现了nextiter方法,迭代器不会一次性把所有元素加载到内存,而是需要的时候才生成返回结果。
    • 生成器是一种特殊的迭代器,它的返回值不是通过return而是用yield。

三元表达式

示例:判断x,y的大小
def max2(x,y):
    if x > y:             #使用if else 的老的写法
        return x
    else:
        return y

    return x if x > y else y      #使用三元表达式写的方式,两边的  x和y只是代表返回值,可以写成字符串形式,那样返回的就是字符串了

print(max2(10,11))

列表解析

示例:把hello所有字符转换成大写并生成list
s = 'hello'
res = [i.upper() for i in s]
print(res)
执行结果:
D:\Python\Python36-32\python.exe E:/Python/DAY-9/day9_笔记.py
['H', 'E', 'L', 'L', 'O']

Process finished with exit code 0

示例:打印出list中大于等于50小于70的数值
l = [1,50,2,3,69,47,32,45,78,80]
print([i for i in l if i >= 50 and i < 70 ])
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-9/day9_笔记.py
# [50, 69]

# Process finished with exit code 0

生成器解析

print([i for i in range(100)])    #生成列表形式

g = (i for i in range(100))    #生成生成器模式,使用for打印出结果   建议使用这种方式,上面的方法如果数字大了很容易卡死电脑
for x in g:
    print(x)

生成器的使用

yield可以接收send发送的值,来触发执行
sendnext的功能,并且还有给yield传值的效果。并且运行时,是先传值在next

def foo():
    print('starting')
    while True:
        x = yield
        print('value: ',x)

g = foo()
next(g)    # 先执行一遍函数
g.send(1)   # 传送数值
g.send(2)
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-10/day10_练习.py
# starting
# value:  1
# value:  2

# Process finished with exit code 0

由于每次执行生成器之前都需要next一下,所以我们可以写个装饰器来干这个事情

def func(func):
    def start(*args,**kwargs):
        g = func(*args,**kwargs)
        next(g)
        return g
    return start

根据上面的原理: 可以实现面向过程编程
模拟查找文件内容包含root的文件

import os        # 导入OS模块
def init(func):       # 生成器初始化装饰器
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper
# 阶段一:递归地找文件的绝对路径,把路径发给阶段二
@init
def search(target):
    'search file abspath'
    while True:
        start_path=yield
        g = os.walk(start_path)
        for par_dir, _, files in g:
            # print(par_dir,files)
            for file in files:
                file_path = r'%s\%s' % (par_dir, file)
                target.send(file_path)    
# 阶段二:收到文件路径,打开文件获取获取对象,把文件对象发给阶段三
@init
def opener(target):
    'get file obj: f=open(filepath)'
    while True:
        file_path=yield
        with open(file_path,encoding='utf-8') as f:
            target.send((file_path,f))

# 阶段三:收到文件对象,for循环读取文件的每一行内容,把每一行内容发给阶段四
@init
def cat(target):
    'read file'
    while True:
        filepath,f=yield
        for line in f:
            res=target.send((filepath,line))   #接收阶段四的tag
            if res:    #判断
                break       #跳出循环,终止本次文件读取循环
 
# 阶段四:收到一行内容,判断root是否在这一行中,如果在,则把文件名发给阶段五
@init
def grep(target,pattern):
    'grep function'
    tag=False
    while True:
        filepath,line=yield tag   #yield返回值为 tag
        tag=False       #初始tag为 False
        if pattern in line:
            target.send(filepath)
            tag=True        #如果判断成功就 把tag变为 True
# 阶段五:收到文件名,打印结果
@init
def printer():
    'print function'
    while True:
        filename=yield
        print(filename)

start_path1=r'C:\Users\Administrator\PycharmProjects\python5期\a'
start_path2=r'C:\Users\Administrator\PycharmProjects\python5期\a\b'
g=search(opener(cat(grep(printer(),'root')))) 

print(g)
# g.send(start_path1)
g.send(start_path2)

递归

在调用一个函数的过程中,直接或间接调用了该函数本身。
递归效率低下,需要在进入下一次递归时保留当前的状态。
Python没有尾递归,并且做了层级限制。
必须有一个明确的结束条件。

l = [1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]]

def readdate(list_name):
    for i in list_name:
        if isinstance(i,list):
            readdate(i)
        else:
            print(i)
readdate(l)

匿名函数

自带return,无需函数名,一定有个返回值。匿名函数只能运行一次。
冒号左边是参数,右边是函数体,函数体执行完毕后直接返回执行的结果。
使用lambda定义匿名函数:
例如: lambda x:x**2

l = [1, 2, 3, 4]
m = map(lambda x: x ** 2, l)
print(list(m))  # [1, 4, 9, 16]

内置函数、模块与包

内置函数链接

内置函数
模块:time;random;hashlib;os
模块:sys,logging,json,pickle
模块:re_1
模块:re_2,subprocess

模块

模块的好处:首先模块可以大大提高代码的可维护性,其次可以减少大量的重复代码,可以通过模块调用来重复使用某些代码。另外,模块还可以避免函数名和变量名冲突。
在Python中一个py文件就称之为一个模块(module)。

注意: 模块的在import的时候,会执行模块内的所有代码,并新建一个名称空间保存这些。类似变量,重复导入同一模块,不会重复执行模块内代码。Python的模块就是天然的单例模式。

模块的搜索路径

模块搜索时会优先搜寻Python标准库(内置库)=>应用程序自定义模块=>第三方模块

查看环境变量

print(sys.path)打印出当前的模块搜索路径,这是个列表可以添加新的变量,直接append就行了

设置别名
import sys as a  # 给sys模块设置别名为a
print(a.path)

按目录组织模块的方法,称为包(Package)。在Python中,包可以理解为一个文件夹,但是这个文件夹中必须要有一个__init__.py文件。

注意: 调用包的时候会执行一下__init__文件内的代码。

类的三大特性: 封装、继承、多态

定义一个基本的类

包含类名,继承的父类(Python3中默认继承object),类静态字段,类方法

class ClassName(object):
    classVal = 123

    def classfunc(self):
        pass

封装

:Python中没有真正的隐藏,仅仅是语法上做了些操作。

在想要封装的变量或者函数名前加上"__"两个下划线,就可以。
加上两个下划线之后,只有在类的定义阶段或者对象的定义阶段(实例化)发生。且在类的外部无法直接调用,但是在类的内部可以直接调用(在定义时全部变形了)
封装其实就是变形

# 运行代码得到结果
class A:
    def foo(self):
        print('from A foo')
        self.__bar()     # 做了隐藏,这时在执行找到就不是bar了,而是 _A__bar,下面的结果能看出来名字被变形了
    def __bar(self):    # 这里等于_A__bar(self)所以最后显示from A bar
        print('from A bar')
class B(A):
    def bar(self):
        print('from B bar')
b = B()
print(A.__dict__)
b.foo()

继承

Python3中的继承关系

如下图继承关系:
[图片上传失败...(image-361b1c-1524577390607)]

根据上图代码示例:
class A:
    def test(self):
        print('from A')
    pass
class B(A):
    def test(self):
        print('from B')
    pass
class C:
    def test(self):
        print('from C')
    pass
class D:
    def test(self):
        print('from D')
    pass
class E(B,C,D):
    def test(self):
        print('from E')
    pass

e = E()
e.test()

执行结果:
默认这样执行,会显示"from E" ,因为调用一个方法会,先从自己的类中查询,下面我们依次注释掉下列类的test:
把E类的test注释掉:结果显示"from B" ,
把B类的test注释掉:结果显示"from A" ,
把A类的test注释掉:结果显示"from C" ,
把C类的test注释掉:结果显示"from D" ,
把D类的test注释掉:结果显示"'E' object has no attribute 'test'"
根据上面的结果过程:在Python3中,当继承多个父类时且父类没有共同的父类时,这时属性的查询顺序是深度优先
上面的属性查询顺序总结:E-->B-->A-->C-->D
这个可能不是很有说服力,那么我们在新建一个F类,让C类继承F类,这时属性的查询顺序如下(仅列出结果):
属性查询顺序:E-->B-->A-->C-->F-->D


当继承多个父类且父类还有共同的父类,如下图
[图片上传失败...(image-822f3b-1524577390608)]

根据上图代码示例:
class A:
    def test(self):
        print('from A')
    pass
class B(A):
    def test(self):
        print('from B')
    pass
class C(A):
    def test(self):
        print('from C')
    pass
class D:
    def test(self):
        print('from D')
    pass
class E(B,C,D):
    def test(self):
        print('from E')
    pass

e = E()
e.test()

执行结果:
默认执行:显示"from E"
把E类的test注释掉:结果显示"from B"
把B类的test注释掉:结果显示"from C"
把C类的test注释掉:结果显示"from A"
把A类的test注释掉:结果显示"from D"
把D类的test注释掉:结果显示"'E' object has no attribute 'test'"
根据上面测试的结果:得出结论在Python3中,当继承多个父类且父类还有共同的父类时,这时属性查找是广度优先
上面的属性查找顺序总结:E-->B-->C-->A-->D

Python2中的继承

新式类:同Python3中一样,是广度优先。所以属性查找顺序是:E-->B-->C-->A-->D
经典类:和新式类恰恰相反,是深度优先。所以属性查找顺序是:E-->B-->A-->C-->D
如下图:
[图片上传失败...(image-b42711-1524577390608)]

子类调用父类的方法

super():内置函数,使用绑定方法调用父类的方法。(仅支持新式类)
注:在Python2中需要写成:super(S1,self).__init__(name,age) # S1 为子类的名字。

class Parent:
    def __init__(self,name,age):
        self.name = name
        self.age = age

class D:
    def walk(self):
        return ('from D')

class S1(Parent,D):
    def __init__(self,name,age,job,school):
        super().__init__(name,age)   #因为使用绑定方法调用父类的方法,所以不需要传递self
        self.job = job
        self.school = school
        super().walk()
t = S1('egon',73,'teach','oldboy')

print(t.name,t.age,t.walk())

多态

多态就是同一类事物的多种形态。
Python本身就是多态的。

python对传入参数的数据类型没有限制,因此,只要是同一类的(比如序列),都可以用一些方法。比如序列:字符串、列表、元组 都有一些方法:obj.len、 切片。那么对于字符串 s, 列表 l,元组 t,都可以通过len(s)、len(l)、len(t) 来获取长度。

一些类方法

类方法列表,这个哥们总结的
__call__方法:

对象后面加括号,触发执行

class Foo:

    def __call__(self, *args, **kwargs):
        print('__call__')

obj = Foo()       
obj()   # 触发执行

__init__方法:

__init__方法在类的一个对象被建立时,马上运行。这个方法可以用来对你的对象做一些你希望的初始化

class Foo:

    def __init__(self):
        print('__init__')

obj = Foo()  # 触发执行

__del__方法:

在对象被删除后调用

class Foo:

    def __del__(self):
        print('__del__')

obj = Foo() 
del obj   # 触发执行

__new__方法:

__new__()__init__()之前被调用,用于生成实例对象,常用于实现单例模式

class Foo:
    __instance = None

    def __new__(cls, *args, **kwd):    # 在__init__之前调用
        if Foo.__instance is None:    # 生成唯一实例
            Foo.__instance = object.__new__(cls, *args, **kwd)
            print('__new__')
        return Foo.__instance

    def __init__(self):
        print('__init__')

Foo()  # 触发执行

__str__():方法:

__str__()用于表示对象代表的含义,返回一个字符串.实现了__str__()方法后,可以直接使用print语句输出对象

class Foo:
    name = 'abc'

    def __str__(self):
        return self.name

print(Foo())  # 触发执行

@property、@staticmethod、@classmethod 三种方法

  • @property
    • @property让类的方法可以不加括号执行
  • @staticmethod
    • @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样
    • 如果需求使用类的一些属性方法,只能直接类名.属性名或类名.方法名来调用
  • @classmethod
    • @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数
    • cls代指的就是类自己本身,可以用过cls点出类的各种属性

反射

主要是指程序可以访问,检测和修改本身状态或行为的一种能力。

class A:
    name = 'abc'
    age = 19

#使用, 类和实例化的对象均可以使用
print(hasattr(A,'n1me'))   #判断类是否含有某个属性   hasattr(类或者对象名,属性名)
print(getattr(A,'name'))   #获取对应属性的属性值    getattr(类或者对象名,属性名,出错返回的内容)  <----出错返回内容如果不写的话,当获取一个不存在的属性会报错,写了就会返回写的内容  
setattr(A,'name','aaa')  #设置属性的属性值    setattr(类或者对象名,属性名,属性值)
print(A.name)
print(A.__dict__)
delattr(A,'name')     #删除属性    delattr(类或者对象名,属性名)
print(A.__dict__)

你可能感兴趣的:(Python基础知识整理)