一、默认参数的陷阱

  先看如下例子:

def func(li=[]):
li.append(1)
print(li) func()
func()
func(li=['abc'])
func()

  结果:

Python学习 day10-LMLPHP

  可以看到,默认参数不传值使用默认值时,多次调用函数li始终使用同一个列表。

  如果默认参数是一个可变数据类型,每一次调用函数时,不传值就公用这个数据类型的资源。

  使用pycharm写代码的时候,若在函数定义时将可变数据类型用作默认参数,pycharm会给出提示:

  Default argument value is mutable

  Inspection info: This inspection detects when a mutable value as list or dictionary is detected in a default value for an argument. Default argument values are evaluated only once at function definition time, which means that modifying the default value of the argument will affect all subsequent calls of the function.

二、三元运算符

  变量 = 条件返回True的结果 if 条件 else 条件返回False的结果,例:

# 定义一个求两数中较大值的函数
def max(a, b):
return a if a > b else b print(max(12, 7))
print(max(3, 5))

  系统中内置函数里也有max()函数,求取得是所有参数中的最大值,也可以传入iterable对象

三、命名空间

内置命名空间

  就是python解释器一启动就可以使用的名字存储在内置命名空间中

  内置的名字在启动解释器的时候被加载进内存里

全局命名空间

  是在程序从上到下被执行的过程中依次加载进内存的

  放置了我们设置的所有变量名和函数名

局部命名空间

  就是函数内部定义的名字

  当调用函数的时候才会产生这个名称空间,随着函数执行的结束,这个命名空间就又消失了

在python解释器下输入import this,可以看到:

>>> import this
The Zen of Python, by Tim Peters Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
>>>

最后一句:

Namespaces are one honking great idea -- let's do more of those!

阐明了命名空间的重要性

四、变量作用域

变量的作用域在每种语言都是一个需要注意的问题,python也一样,python中变量的作用域依赖于命名空间,自己知道的js有命名空间,其他语言就不清楚了。

  • 在局部:可以使用全局、内置命名空间的名字
  • 在全局:可以使用内置命名空间中的名字,但是不能用局部中的名字
  • 在内置:不能使用局部和全局的名字

在之前学习的可直接调用的方法、关键字都是存在于内置命名空间的,如print(),input(),del,def,for...in...等,所以我们可以直接调用

另外,对于已有定义的函数,都可以在能够使用它的命名空间里对它进行覆写,例:

def max(*args):
print('max1') def max(*args):
print('max2') max(1, 3)

结果:

Python学习 day10-LMLPHP

如上,在全局命名空间里我们定义了两次max()函数,最终max(1, 3)调用的方法是最后一个max(),即每次定义都对原有名字进行了覆盖。

可以说,遵循的就是一个就近原则

作用域:

  • 全局作用域 —— 作用在全局 —— 内置和全局名字空间中的名字都属于全局作用域 —— globals()查看
  • 局部作用域 —— 作用在局部 —— 函数(局部名字空间中的名字属于局部作用域)—— locals()查看

综上,因为依赖倒置的关系,内部的命名空间可以使用外部的命名空间中的变量,反之不行,即有:

a = 1

def func():
print(a) func()

func()是可以正常获取a的值并打印的,但是,能获取使用并不代表可以修改,如下:

a = 1

def func():
a = 2
print(a) func()
print(a)

结果:

Python学习 day10-LMLPHP

可以看到,函数内部虽然a的值为2,但对于全局命名空间中的a,值依然是1,说明在func()内部并没有修改变量a的值。

但目前存在我们需要对外部变量修改的情况,在python中也是提供了解决方法的:

a = 1  # 全局中是否有变量a都一样

def func():
global a # 声明全局变量a
    a = 2
print(a) func()
print(a)

结果:

Python学习 day10-LMLPHP

可以看到,在函数内部将变量a声明为global后,a即为全局变量,全局作用域中有无a都一样,在函数内部的修改也会对全局作用域中的a进行修改。

注意:以上只针对不可变数据类型适用

在局部作用域虽然不能直接修改全局作用域中不可变数据类型的值,但是对可变的数据类型,是可以进行修改的,如下:

a = [1, 2]

def func():
a.append(1)
print(a) func()
print(a)

结果:

Python学习 day10-LMLPHP

可以看到,对于可变数据类型,在函数内部是可以对全局作用域中的变量进行修改的。

总结:

1、对于不可变数据类型,在局部可以查看全局作用域中的变量,但是不能直接修改;

2、如果想要修改,需要在函数的一开始添加global声明;

3、如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效。

globals() -- 得到所有的全局变量

locals() -- 得到所有的本地变量 -- 意思是在函数内获取即得到本函数的作用域下的变量,在全局获取得到的与globals()结果相同,也是全局变量

五、函数的嵌套调用

即函数之间可以互相调用,只要是能获取的名字都可以,如:

def larger(a, b):  # 返回较大值
return a if a > b else b def max(x, y, z): # 返回最大值
c = larger(x, y) # 嵌套调用,调用larger()
return larger(c, z)

六、函数的嵌套定义

嵌套定义,即在函数内部定义函数,例:

def outer():
def inner():
print('inner')
inner() outer()

根据依赖倒置原则,嵌套定义的内部函数可以使用外部函数的变量,但是对外部函数中不可变数据类型的变量的修改,将不能再使用global声明:

def outer():
a = 1 def inner():
global a
a = 2
inner()
print(a) outer()
print(a)

结果:

Python学习 day10-LMLPHP

可以看到,在函数内部定义的函数,使用global声明变量,变量依然是全局变量。

若想修改外部函数的变量,则不能使用global修饰了,而应用nonlocal:

a = 0

def outer():
a = 1 def inner():
nonlocal a
a = 2
inner()
print(a) outer()
print(a)

结果:

Python学习 day10-LMLPHP

nonlocal只能用于局部变量,找上层中离当前函数最近一层的局部变量

声明了nonlocal的内部函数的变量修改会影响到离当前函数最近一层的局部变量

七、第一类对象(first-class object)

第一类对象指:

  • 可在运行期创建
  • 可用作函数参数或返回值
  • 可存入变量的实体

在python中,函数满足以上3点,因此是第一类对象。

八、闭包

闭包的概念:嵌套函数,内部函数调用外部函数的变量。例:

def outer():
a = 1 def inner():
print(a)
print(inner.__closure__) outer()
print(outer.__closure__)

结果:

Python学习 day10-LMLPHP

inner函数调用outer函数的变量a,就形成了一个闭包。打印函数的__closure__,出现cell at xxxxxx 就说明是一个闭包。

 闭包的常用方式

先看例子:

def outer():
a = 1 def inner():
print(a)
return inner # inner函数作为返回值返回 func = outer() # 实际func指向了inner函数
func() # 调用的是inner

  这种在全局调用内部函数的方式是闭包最常用的方式,因为:

1、如果在outer内部调用inner,每次想使用inner都必须频繁地开空间关空间,浪费时间;

2、同时在外部调用闭包可以保护像a这种变量,a虽然不是全局变量,却始终存储在内存里,需要时即可调用,延长了a的生存周期。

九、零散的知识点

1、urlopen

获取网页的源代码:

# 使用urllib模块获取网页内容
# 结合闭包的内容,保存url信息,不需要每次调用重新开辟空间 from urllib.request import urlopen def outer(url):
def inner():
return urlopen(url).read() # 获取网页源代码
return inner func = outer('http://www.taobao.com') # func = inner,url会一直保存在内存
print(func()) # 后续调用即调用inner,url始终是'http://www.taobao.com'
print(func())
print(func())
print(func())

结果:

Python学习 day10-LMLPHP

其实就是目前需要知道的就是:urlopen(网址).read(),得到的网页内容,可以看到是bytes类型的

2、dic循环

for循环dict时,尽量不要循环values(),因为value一般值比较大,会占用更多的内存

十、程序设计六大原则

1、依赖倒置原则(DIP)

  依赖倒置原则,就目前自己了解就是内部的依赖于外部的,反之不行。

  如命名空间:在局部命名空间里可以使用全局命名空间和内置命名空间里的名字,全局命名空间可以使用内置命名空间里的名字,而内置命名空间只能使用自己空间里的名字。这就是依赖倒置。

  DIP是6大原则中最难以实现的原则,它是实现开闭原则的重要途径,DIP没有实现,就别想实现对扩展开放,对修改关闭。在项目中只要记住“面向接口编程”就基本上抓住了DIP的核心。

ps:目前只知道这个原则,后续补充

05-07 15:41