Python 学习笔记(作用域、函数、类)

记一些 Python 的学习笔记,关于作用域、函数以及类:


作用域

  • 函数查找变量:当前函数局部变量 —— 包含函数的局部变量 —— 全局变量 —— 内置名字表

所有函数中的变量赋值都是将值存储在当前函数局部变量内,全局变量不能在函数中直接赋值(除非用 global 语句命名),但是可以引用。

global

global 表示该变量为全局作用域

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

以上输出结果:

1
123

nonlocal

nonlocal 表示该变量为外层作用域

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

以上输出结果:

100
100

函数

#定义
def 函数名(参数) :
    '''描述'''
    缩进的逻辑语句
  • 返回:不指定 return 的返回值会默认返回 None

参数

  • 位置参数
# 定义
def add(x, y):
    '''一个加法函数'''
    return x + y

# 调用
add(1, 2) # 1对应x, 2对应y
  • 默认参数
# 必选参数 在 默认参数 前
def add(x, y=3):
    '''一个加法函数'''
    return x + y

# 调用
add(1, 2) # 1对应x, 2对应y

add(1)    # 1对应x,y默认为3

定义默认参数要牢记一点:默认参数必须指向不变对象!因为默认值只被赋值一次。

  • 可变参数

传入的参数个数是可变的。

# 在函数内部,参数 args 接收到的是一个 tuple
def add(*args):
    sum = 0
    for n in args:
        sum = sum + n
    return sum

# 调用
add(1, 2, 3)     # 参数 args: (1, 2, 3)

# 已有一个序列
nums = [1, 2, 3]
add(*nums)       # *nums 表示把 nums 这个 list 的所有元素作为可变参数传进去
  • 关键字参数

关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

def person(**kw):
    print(kw)

# 调用
person(name='Ben', age=10)  # 输出 {'name': 'Ben', 'age': 10}
person()                    # 输出 {}

# 已有字典
Ben = {'name': 'Ben', 'age': 10}
person(**Ben)

'''
**Ben表示把Ben这个dict的所有key-value用关键字参数传入到函数的**kw参数kw将获得一个dict
注意kw获得的dict是Ben的一份拷贝,对kw的改动不会影响到函数外的Ben
'''
  • 命名关键字参数

要限制关键字参数的名字,就可以用命名关键字参数

# 和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。
def person(name, age, *, city, job):
    print(name, age, city, job)

# 调用
person('Jack', 24, city='Beijing', job='Engineer')

# 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了
def person(name, age, *args, city, job):
    print(name, age, args, city, job)

# 调用
person('Jack', 24, city='Beijing', job='Engineer')

命名关键字参数必须传入参数名,否则报错。

参数定义的顺序必须是:必选位置参数、默认参数、可变参数、命名关键字参数、关键字参数。

递归

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

解决递归调用栈溢出的方法是通过尾递归优化。尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

def fact(n):
    return fact_iter(n, 1)

def fact_iter(num, product):
    if num == 1:
        return product
    return fact_iter(num - 1, num * product)

匿名函数

  • 主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
  • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
lambda [arg1 [,arg2,.....argn]]:expression

  • 获取类型
type(123)
# <class 'int'>
type(abs)
# <class 'builtin_function_or_method'>
  • 判断函数
import types

def fn():
    pass

type(fn) == types.FunctionType # True
  • 判断 class 类型

    isinstance() 判断的是一个实例对象是否是该类型,或者位于该类型的父继承链上。

    isinstance(对象, 类型)

    还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是 list 或者 tuple

    isinstance([1, 2, 3], (list, tuple)) 

    issubclass() 判断的是一个类是否某类型的子类型。

    issubclass(子类, 父类)

封装

class Student(object):
    a = 123                          # 类属性
    def __init__(self, name, score): # 定义实例属性
        self.name = name
        self.__score = score         # 内部属性 外部不能访问
    def print_score(self):
        print('%s: %s' % (self.name, self.__score))

继承

如下,obj 是父类,Student 会继承 obj 的所有方法和属性。

class Student(obj):
    pass

但在 Student 中定义的和父类重名方法或重名属性,会优先用 Student 的中的方法和属性。

  • 多继承
class DerivedClassName(Base1, Base2, Base3):
    pass

从优先级左到右继承,比如某方法或属性,当前类中找不到,去 Base1 找,也没有就去 Base2 找,以此类推。

多态

如下,AB 的父类。

class A:
    def do(self):
        pass

class B(A):
    pass

a = A()
b = B()

def Do(obj):
    obj.do()

# Do 函数接受一个对象,执行对象的do方法。
# 只要是A类或者继承自A类,都可以作为参数obj

猜你喜欢

转载自blog.csdn.net/aq115aq/article/details/80322423