赞
踩
python的容器拢共有5个:
声明符号:list(列表),set(集合),dict(字典),tuple(元组),str(字符串)
字符串(str):所谓字符串,通过字符之间自由的组合在一起构成字符串,字符串是不可变数据类型,也就意味着不可修改元素,字符串也是一个有序序列
列表(list):线性表, 底层采用双向循环链表进行实现。列表当中可以存放不同种类的数据,例如lst=[1, ‘list’,[1,2,3]],其中的每个元素都分配一个数字 - 它的索引,从0开始。同Java语言中的数组是一样的,唯一的不同之处在于,Java中的数组类型只能放置同一种数据类型。
元组(tuple): 元组一旦创建,那么里面的数据保守意义上来说是不可变得,但是如果元祖里面存储的是可变数据类型,那么元祖即又变得可变。与列表类似,不同之处在于元组的元素不能修改。
集合(set):集合的底层基于hash表实现的,不能重复,无序的。集合内数据无序,即无法使用索引和分片。
字典(dict):以键值对形式进行数据存储。散列表(或者hash哈希表),其中的键确保是唯一的,且必须是不可变的,值可以取任何数据类型。
索引
所谓“索引”,就是在序列中,根据所需元素的下标
切片slice
切片,就是在序列中切一块
作用:根据两个索引值获取容器内的元素块
语法:容器[(索引值1):(索引值2)(:步长)]
切片的语法:[起始:结束:步长]
1、一个参数:a[i]
如 [2],将返回与该索引相对应的单个元素。
2、两个参数:b=a[i:j]
b = a[i:j] 表示复制a[i]到a[j-1],以生成新的list对象
i缺省时默认为0,即 a[:n] 代表列表中的第一项到第n项,相当于 a[0:n]
j缺省时默认为len(alist),即a[m:] 代表列表中的第m+1项到最后一项,相当于a[m:5]
当i,j都缺省时,a[:]就相当于完整复制a
3、三个参数:格式b = a[i:j:s]
这里的s表示步进,缺省为1.(-1时即翻转读取)
所以a[i:j:1]相当于a[i:j]
当s<0时,i缺省时,默认为-1. j缺省时,默认为-len(a)-1
所以a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍。所以你看到一个倒序的东东。
1、该容器对象通过__iter__()方法获取了一个迭代器it,it迭代器中有两个方法:next()和__iter__()。
2、for in语句简化了迭代
3、enumerate(列举)
enumerate(iteration, start)函数默认包含两个参数,其中iteration参数为需要遍历的参数,比如字典、列表、元组等,start参数为开始的参数,默认为0(不写start那就是从0开始)。enumerate函数有两个返回值,第一个返回值为从start参数开始的数,第二个参数为iteration参数中的值。
b = [1,2,3,4,5,6]
for i , item in enumerate(b):
print(i, item)
在Python中生成器有两种类型:生成器函数以及生成器表达式。生成器函数就是包含yield参数的函数。生成器表达式与列表解析式类似。
1、而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数、for循环、list()等方法使用。
2、or 循环无法实现的时候,还可以用函数来实现。
在使用生成器实现的⽅式中,我们将原本在迭代器 next ⽅法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在def中有yield关键字的 就称为 生成器此时按照调用函数的方式( 案例中为F = fib(5) )使用生成器就不再是执行函数体了,而是会返回一个生成器对象( 案例中为F ),然后就可以按照使用迭代器的方式来使用生成器了
参考链接:https://blog.csdn.net/qwertyuiopasdfgg/article/details/89326025
for的三种常用
python中for的三种常用遍历方式( for … in,for … in range(),for … in enumerate() )
list = ['html', 'js', 'css', 'python'] # 方法1 #for … in #作用是在每一次的循环中,依次将 in 关键字后面序列变量的一个元素赋值给 for 关键字后的变量。 for i in list: print ("序号:%s 值:%s" % (list.index(i) + 1, i)) # 方法2 # range(a, b,c) 函数中,a、b分别为遍历的左右区间阈值(左闭右开,即从a开始到b-1结束),c为遍历的步长。一般只写b,默认a=0、c=1,即range(10)取1到9的所有数。 # 如:for i in range(1, 5, 2) # 此时 i 可以取的值为1,3。 for i in range(len(list)): print ("序号:%s 值:%s" % (i + 1, list[i])) # 方法3 #for i, b in enumerate(a) 方式需要同时对 i,b两个变量同时赋值,i 赋值为a当前元素的下标,b赋值为a当前的元素。 for i, val in enumerate(list): print ("序号:%s 值:%s" % (i + 1, val))
同步遍历多个列表
Python同步遍历多个列表
zip(列表 1,列表 2,…) 将多个列表对应位置的元素组合成为元组,并返回这个 zip 对象。
#1、Python的for循环十分灵活,使用for循环我们可以很轻松地遍历一个列表,例如: a_list = ['z', 'c', 1, 5, 'm'] for each in a_list: print(each) #2、①使用zip()函数 list1 = [1, 2, 3, 4] list2 = [9, 8, 7, 6] z = zip(list1, list2) print(z) print(list(z)) for i, j in z: print(i + j) 其运行结果如下: <zip object at 0x00000159B3B5C348> [(1, 9), (2, 8), (3, 7), (4, 6)] Process finished with exit code 0 #3、②利用下标 list1 = [1, 2, 3, 4, 5] list2 = ['a', 'b', 'c', 'd', 'f'] n = 0 for each in list1: print(each, list2[n]) n += 1
全局变量 作用域
全局变量为定义在函数外部的变量
局部变量为定义在函数内部的变量
所以书写时全局变量大写,局部变量小写清晰方便让他人读懂
golobal 指定全局变量
nonlocal 指定上一级变量
第一,两者的功能不同。global关键字修饰变量后标识该变量是全局变量,对该变量进行修改就是修改全局变量,而nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误(最上层的函数使用nonlocal修饰变量必定会报错)。
第二,两者使用的范围不同。global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用,而nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误(见第一)。
匿名函数
lambda函数:lambda函数是一种匿名函数,即没有名字的函数。使用lambda保留字定义,函数名是返回结果;lambda函数用于定义简单的,能够在一行内定义的函数;lambda函数主要用作一些特定函数或方法的参数。
<函数名> = lambda <参数> : <表达式>
例1:f = lambda x , y : x + y
调用:f(6 + 8) 输出:14
将lambda函数作为参数传递给其他函数。
filter函数。此时lambda函数用于指定过滤列表元素的条件。
sorted函数。此时lambda函数用于指定对列表中所有元素进行排序的准则。
map函数。此时lambda函数用于指定对列表中每一个元素的共同操作。
reduce函数。此时lambda函数用于指定列表中两两相邻元素的结合条件。
参:https://zhuanlan.zhihu.com/p/163753262
闭包
闭包(closure)是函数式编程的重要的语法结构。当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用 域之外执行。函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
我们定义了一个函数 func,同时,在函数 func 内部又定义了一个局部函数 func_inner,最后,我们在函数 func 里面返回了函数 func_inner。这样,我们在函数里面返回了一个局部函数,就构成了一个闭包。
一个包里可以有多个模块,每个模块可以定义一些函数完成特定功能。
函数、变量、类存储在被称为模块(Module)的.py文件中,一组模块文件又组成了包(Package)。
模块
Python 中一个以 .py 结尾的文件就是一个模块,模块中定义了变量、函数等来实现一些类似的功能。Python 有很多自带的模块(标准库)和第三方模块,一个模块可以被其他模块引用,实现了代码的复用性。
包
包是存放模块的文件夹,包中包含 init.py 和其他模块,init.py 可为空也可定义属性和方法,在 Python3.3 之前的版本,一个文件夹中只有包含 init.py,其他程序才能从该文件夹引入相应的模块、函数等,之后的版本没有 init.py 也能正常导入,简单来说就是 Python3.3 之前的版本,init.py 是包的标识,是必须要有的,之后的版本可以没有。
参考链接:https://blog.csdn.net/ityard/article/details/103502449
引用方式有两种:
从包中引入模块有如下两种方式:
1.import …
import 包名1.包名2…模块名
2.from … import …
from 包名1.包名2… import 模块名
from 包名1.包名2…模块名 import 变量名/函数名
模块导入方法
import module_name
1.使用:module_name.函数名/变量名/类名
from module_name import 函数名/变量名/类名
2.使用:直接通过 函数名/变量名/类名使用
from module_name import *
3.使用:直接通过 函数名/变量名/类名使用
导入包的格式
1.import package_name.module_name as 简写
简写.变量名/函数名/类名
2.from package_name.module_name import
变量名/函数名/类名
3.from package_name.module import *
变量名/函数名/类名
4.from package_name import module_name
module_name.变量名/函数名/类名
5.from package import *
module_name.变量名/函数名/类名
参考链接:https://blog.csdn.net/Artificial_idiots/article/details/111807822
类的成员包括:属性和方法。
属性可以分为:静态属性和实例属性
方法可以分为:普通方法、类方法和静态方法。
实例属性属于对象,而静态属性属于类。
class Person:
# 类属性,通过类名访问,属于整个类,而不是某个对象
nation = ‘中国’
class Foo: def __init__(self, name): self.name = name def ord_func(self): """ 定义普通方法,至少有一个self参数 """ # print self.name print('普通方法') @classmethod def class_func(cls): """ 定义类方法,至少有一个cls参数 """ print('类方法') @staticmethod def static_func(): """ 定义静态方法 ,无默认参数""" print('静态方法')
链接:https://blog.csdn.net/xiaoxianer321/article/details/117237251
静态方法:类似类方法 1.需要装饰器@staticmethod 2.静态方法无需传递参数 3.也只能访问类的属性和方法,对象的是无法访问的 4.在对象创建之前已经被加载 总结: 类方法 静态方法 不同: 1.装饰器不一样 2.类方法有参数,静态方法没有参数 相同: 1.只能访问类的属性和方法,对象的是无法访问的 2.都可以通过类名调用访问 3.都可以在创建对象之前使用,不依赖于对象。
普通方法与两者的区别: 不同: 1.没有装饰器 2.普通方法永远要依赖对象,因为每个普通方法都有一个self 3.只有创建了对象才可以调用普通方法,否则无法调用 。
说明:
使用staticmethod装饰器装饰的方法(方法没有cls参数)
通过类名进行调用
说明:将成员方法当做属性一样进行访问
作用:保护特定属性,或者对特定属性进行处理
class A:
@staticmethod
def demo():
print('类来调用')
a = A()
a.demo()
A.demo()
继承:父类的属性和方法,子类直接拥有,称为继承
派生:子类在父类的基础上衍生出新的特征(属性和行为)
总结:其实他们是一回事,只是描述问题的侧重点不同(继承强调相同点,派生强调不同点)
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承用于指定一个类将从其父类获取其大部分或全部功能。
只需要继承基类这个动作,就可以访问到基类的属性和方法了,它提高了代码的可扩展性。
在 python 中继承中的一些特点:
• 如果派生类中有__init__() 方法,那么基类的 init() 方法不会被自动调用,它需要在其派生类的构造中专门调用。
• 如果派生类中没有 init() 方法 , 且基类中有 init() 方法,那么基类的 init() 方法会被自动调用
• 调用父类的方法有三种:
方法1:
父类名字 . 父类中的方法名 (self,[ 参数 1 ,参数 2 ,参数 3,……])
方法 2 :
super(). 父类中的方法名 ([ 参数 1 ,参数 2 ,参数 3,……])
方法 3 :
super( 当前类的名字 ,self). 父类中的方法名 ([ 参数 1 ,参数 2 ,参数 3,……])
class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender print("Person类__init__()。", "姓名:", self.name) class Student(Person): def __init__(self, name, gender, score): super().__init__(name, gender) self.score = score print("Student类__init__()。", "姓名:", self.name) student = Student('tom','male',10)
**多态是指基类的同一方法在不同派生类对象中具有不同的变现和行为**。不同的派生类对象调用相同的基类方法,产生了不同的执行结果,这样可以增加代码的外部调用灵活度,多态以**继承和重写父类方法**为前提条件,多态只是调用方法的技巧,不会影响到类的内部设计。
class Dog: def work(self): pass class ArmyDog(Dog): def work(self): print("追击敌人。") class DrugDog(Dog): def work(self): print("追查毒品。") class Person: # 只要能接收父类对象,就能接收子类对象 def work_with_dog(self, dog): # 只要父类对象能工作,子类对象就能工作。并且不同子类会产生不同的执行效果。 dog.work() person = Person() person.work_with_dog(ArmyDog()) person.work_with_dog(DrugDog())
参考链接:https://blog.csdn.net/IT_in1314/article/details/119809208
公有的:类中的普通的属性和方法,默认都是公有的;可以在类内、类外、子类中使用
私有的:定义时在前面添加两个’_’,就变成了私有的;只能在类内使用,不能在类外及子类中使用
class Person: def init(self, name): self.name = name self.__age = 20 def eat(self): print('民以食为天') def __test(self): print('__test') xiaoming = Person('小明') print(xiaoming.name) xiaoming.eat() #不能在类外使用 #print(xiaoming.__age) xiaoming._Person__test() print(xiaoming.dict) #尽管可以这样访问私有属性,但是强烈建议不要这样使用 #print(xiaoming._Person__age) class Man(Person): def introduce(self): # 不能在子类中使用 # print(self.__age) print('我叫{}'.format(self.name)) self.eat() m = Man('亮亮') m.introduce() 链接:https://blog.csdn.net/qq_41138285/article/details/83892679
dict : 类的属性(包含一个字典,由类的数据属性组成)
doc :类的文档字符串
name: 类名
module: 类定义所在的模块(类的全名是’main.className’,如果类位于一个导入模块mymod中,那么className.module 等于 mymod)
bases : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
常用的属性
doc 类的说明
__name__返回类名
base 返回一个父类
bases 返回多个父类
dict 返回对象或者类的信息
init 构造方法 作为类的初始化
del 析构方法 在当前文件执行完毕之前去执行 或者是使用 del 对象名 去触发
str 用于转换成 人类能够阅读的形式
repr 转换成解释器查看的形式
add 运算符重载
getattr 调用不存在的属性的时候触发
len
getitem
setitem
魔法函数写在类里面,所以 init 函数的至少有一个参数 self 。
通过参数给要修改的属性传参,一般参数名往往和属性名一致
一旦写了__init__函数,并且__init__函数除了self以外还有其他参数,就需要为__init__函数除了self以外的参数传入值。
也可以给__init__函数的参数设置默认值。
str 函数,该函数要求返回一个字符串,这个字符串的信息要能够把对象的信息组织起来,
当我们使用 print 函数打印对象的时候,实际上打印的就是 str 函数返回的字符串。
链接:https://blog.csdn.net/weixin_42105064/article/details/80151587
Python实现多任务(协程、线程、进程)
进程: 进程是一个具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统资源分配和独立运行的最小单位;
线程: 线程是进程的一个执行单元,是任务调度和系统执行的最小单位;
协程: 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
线程有时被称为轻量级进程( Lightweight Process, LWP),是程序执行流的最小单元。线程拥有自己独立的栈和共享的堆,共享堆,不共享栈。线程的切换一般也由操作系统调度。线程具有5种状态:初始化、可运行、运行中、阻塞、销毁。
对操作系统而言,线程是最小的执行单元,进程是最小的资源管理单元。无论是进程还是线 程,都是由操作系统所管理的。
协程是一种比线程更加轻量级的一种函数。正如一个进程可以拥有多个线程一样,一个线程可以拥有多个协程。协程不是被操作系统内核所管理的,而是完全由程序所控制的,即在用户态执行。 这样带来的好处是:性能有大幅度的提升,因为不会像线程切换那样消耗资源。
协程不是进程也不是线程,而是一个特殊的函数。这个函数可以在某个地方被“挂起”,并且可以重新在挂起处外继续运行。所以说,协程与进程、线程相比并不是一个维度的概念。
一个进程可以包含多个线程,一个线程也可以包含多个协程。简单来说,在一个线程内可以有多个这样的特殊函数在运行,但是有一点必须明确的是:一个线程中的多个协程的运行是串行的。 如果是多核CPU,那多个进程或一个进程内的多个线程是可以并行运行的。但是在一个线程内协程 却绝对是串行的,无论CPU有多少个核毕竟协程虽然是一个特殊的函数,但仍然是一个函数。 一个线程内可以运行多个函数,但这些函数都是串行运行的。当一个协程运行时,其他协程必须被挂起。
一个进程可以包含多个线程,一个线程可以包含多个协程。虽然一个线程内的多个协程可以切换但是这多个协程是串行执行的,某个时刻只能有一个线程在运行,没法利用CPU的多核能力。
协程既不是进程也不是线程,协程仅是一个特殊的函数。协程、进程和线程不是一个维度的。协程与进程一样,也存在上下文切换问题。进程的切换者是操作系统,切换时机是根据操作系统自己的切换策略来决定的,用户是无感的。进程的切换内容包括页全局目录、内核栈和硬件上下文,切换内容被保存在内存中。 进程切换过程采用的是“从用户态到内核态再到用户态”的方式,切换效率低。
线程、进程、协程的区别
进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是这样的)。
协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。
一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间是平行运行的,在线程里面可以开启协程,让程序在特定的时间内运行。在单CPU上,是os代码强制把一个进程或者线程挂起,换成另外一个来计算,所以,实际上是串行的,只是“概念上的并行”。在现在的多核的cpu上,线程可能是“真正并行的”。
协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。
打个比方吧,假设有一个操作系统,是单核的,系统上没有其他的程序需要运行,有两个线程 A 和 B ,A 和 B 在单独运行时都需要 10 秒来完成自己的任务,而且任务都是运算操作,A B 之间也没有竞争和共享数据的问题。现在 A B 两个线程并行,操作系统会不停的在 A B 两个线程之间切换,达到一种伪并行的效果,假设切换的频率是每秒一次,切换的成本是 0.1 秒(主要是栈切换),总共需要 20 + 19 * 0.1 = 21.9 秒。如果使用协程的方式,可以先运行协程 A ,A 结束的时候让位给协程 B ,只发生一次切换,总时间是 20 + 1 * 0.1 = 20.1 秒。如果系统是双核的,而且线程是标准线程,那么 A B 两个线程就可以真并行,总时间只需要 10 秒,而协程的方案仍然需要 20.1 秒。
协程的好处:
无需线程上下文切换的开销
无需原子操作锁定及同步的开销
方便切换控制流,简化编程模型
高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:
无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
而服务器端开发中,大多数时候都是要花大量等待时间的场景,也就是所谓的IO密集,协程极为适合这种场景,而go又主打协程,直接从语法层面支持,切中了以往开发高性能程序太过于复杂的痛点,因此广受程序员们的欢迎。java其实也可以模拟出协程的效果,比如用nio和多线程,也能假装goroutines的效果,但实际操作起来太过于麻烦,还要掌握一大堆枯涩的概念,完全没有goroutines的优雅。所以在并发性能上,go完胜java。换言之,go比java更适应高并发场景,能更优雅方便的写出高并发程序。
参考
链接:https://www.jianshu.com/p/e2fb46e10878
链接:https://juejin.cn/post/6975852498393235487
阻塞与非阻塞
阻塞是指调用线程或者进程被操作系统挂起。
非阻塞是指调用线程或者进程不会被操作系统挂起。
同步与异步
同步是阻塞模式,异步是非阻塞模式。
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,知道收到返回信息才继续执行下去;
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回式系统会通知进程进行处理,这样可以提高执行的效率。
操作系统中堆和栈的区别:
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap)— 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
进程空间内存结构
Text(代码段):存放可执行的指令操作,只能读不能写;
Bss(静态区或全局区):存放未初始化的全局变量和静态变量;
Data(数据区):存放初始化的全局变量和静态变量(全局和局部)、常量数据;
Stack:存放临时变量、函数参数等;在程序块开始时自动分配内存,结束时自动释放内存,其操作方式类似于数据结构中的栈。
Heap:存放new、malloc等动态申请的变量,用户必须手动进行delete、free操作。
Stack和Heap的内存增长方向是相反的。
初始化就是“第一次赋值”的意思,初始化变量是指为变量指定一个明确的初始值。 初始化变量有两种方式:一种是声明时直接赋值,一种是先声明、后赋值。
初始化:全局初始化数据区/静态数据区.加载的是可执行文件数据段,位置可位于代码段后也可以分开.程序在运行之初就为该数据段申请了空间,在程序退出时才释放,因此,存储于数据段的数据的生存周期为整个程序运行过程.
未初始化:未初始化数据区.加载的是可执行文件BBS段,位置可以分开也可以紧靠数据段.程序在运行之初为该部分申请了空间,在程序退出时才释放,存储于该部分的数据的生存周期为整个程序运行过程.给其赋0值【在有些编译器中,初始化为0的静态变量和全局变量也放在.bss段】
(1)全局初始化数据区/静态数据区:只初始化一次。
(2)未初始化数据区(BSS):在运行时改变其值。
全局变量、文件域的静态变量和类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化;局部静态变量(一般为函数内的静态变量)在第一次使用时分配内存并初始化。这里的变量包含内置数据类型和自定义类型的对象。
C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由 编译器自动分配释放 ,存放 函数的参数名, 局部变量的名等。其操作方式类似于 数据结构中的栈。
2、堆区(heap)— 由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与 数据结构中的堆是两回事,分配方式倒是类似于 链表。
3、全局区( 静态区)(static)— 全局变量和 静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、文字常量区—常量字符串就是放在这里的,程序结束后由系统释放 。
5、程序代码区— 存放 函数体的 二进制代码。
链接:https://blog.csdn.net/speedme/article/details/22943191/
可能产生的问题
内存泄漏
当程序中使用malloc的时候,堆(heap)会向上增长,其增长的部分就成为malloc从内存中分配的空间。malloc开辟的空间会一直存在,直到程序员手工用free系统调用来释放,或者进程结束。
内存泄漏(memory leakage), 就是指我们没有释放不再使用的堆空间,导致堆不断增长,而内存可用空间不断减少。
栈溢出
栈和堆的大小则会随着进程的运行增大或者变小。当栈和堆增长到两者相遇时候,也就是内存空间图中stack和heap之间的可用内存区域完全耗尽时,进程会出现栈溢出(stack overflow)的错误,导致进程终止。
垃圾回收机制(Garbage-Collection)
由上文可知,进程的内存管理是十分重要的,内存需要被合理分配使得进程能够正常运行,避免出现内存泄漏、栈溢出等异常情况。
内存空间是有限的,不能一味地分配内存,需要有人负责回收分配出去的内存空间,如果交由程序员手动进行内存管理,程序员就比较累,没法完全专注于业务逻辑的实现,影响开发效率,而且手动管理内存是纯技术活,人工错误很常见,这就有了垃圾回收机制(Garbage-Collection),程序员只要专注于业务逻辑的实现,尽管用内存,不必关心内存的回收。
链接:https://www.jianshu.com/p/4e1c751d85f8
作用域来看
局部变量:定义在函数内部的变量(函数的形参也是局部变量),只能在定义它的函数内部使用
全局变量:定义在函数外面的变量,所有函数都可以使用
静态变量:有全局变量、前面加了“static”关键字的局部变量
区别:
静态变量的存放地址,在整个程序运行期间,都是固定不变的。
非静态变量(一定是局部变量)地址每次函数调用时都可能不同,只在函数的一次执行期间不变
如果没明确初始化,则静态变量会被自动初始化成全0(每个bit都是0),局部非静态变量的值则随机
不管加不加static,全局变量都是存储在静态存储区的,都是在编译时分配存储空间的,两者只是作用域不同,全局变量默认具有外部链接性,作用域是整个工程,全局静态变量的作用域仅限本文件,不能在其他文件中引用。全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。
static全局变量与普通的全局变量
static全局变量只初使化一次,作用域被限制在该变量的源文件内有效,防止在其他文件单元中被引用
static局部变量和普通局部变量
static局部变量只被初始化一次,下一次依据上一次结果值
static函数与普通函数
static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
静态变量在类中,不属于实例对象,属于类所有,只要程序加载了字节码,不用创建实例对象静态变量就会被分配空间,已经可以使用。
实例变量是某个对象的属性,只有实例化对象后,才会被分配空间,才能使用。
类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;
而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象;
1、static是一个修饰符,static修饰的成员变量称之为静态变量或类变量。
2、static修饰的成员被所有的对象共享。
3、static优先于对象存在,因为static的成员随着类的加载就已经存在。
4、static修饰的成员多了一种调用方式,可以直接被类名所调用(类名.静态成员)。
5、static修饰的数据是共享数据,对象中的存储的是特有的数据。
1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。 大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数 据和代码范围的好处和重要性。
Python全面知识点总结参考:https://blog.csdn.net/Artificial_idiots/article/details/111807822
if __name__ == '__main__': # print_hi('PyCharm') # 导入包 import argparse # 创建解析器 ap = argparse.ArgumentParser() # 添加位置参数(positional arguments) ap.add_argument("-i", "--input", required=False, help="path to the input folder") ap.add_argument("-m", "--model", required=False, help="path to the model file") args = vars(ap.parse_args()) args2=ap.parse_args() print(args) print(args["input"], args["model"]) print(args2.input,args2.model)
1.命令行
python main.py -i “我是i” -m “我是m”
2.pycharm配置
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。