函数
python中『一切皆对象』, 函数也不例外.
在之前所学的 C++ 或 Java 中, 可以发现函数的返回值要么为空, 要么是某种数据类型, 但是在 python 中, 返回值可以是任何对象, 包括 函数 .
函数参数
函数的参数种类比较多, 主要有:
1.位置参数 ( positional argument ): 就是最常见的x, y等
2默认参数 ( default argument ): 给定一个默认值, 用户也可以传入实参来调整.
def func(x, y=3): print(x+y) func(1) # 4 func(1, 666) # 667
3.可变参数 ( variable argument ): 不限制输入参数的个数, 传入后自动保存为 元组类型 .
1. *args 是可变参数, args 接收的是一个 tuple
def printinfo(arg1, *args): print(arg1) print(args, type(args)) printinfo(10) # 仅一个参数, 没有属于args的. # 10 # () <class 'tuple'> printinfo(70, 60, 50) # 除arg1位置匹配的, 其他都传入给可变参数 # 70 # (60, 50) <class 'tuple'>
4.关键字参数 ( keyword argument ): 不限制关键字的个数和命名, 传入后自动保存为字典的形式.
1. **kw 是关键字参数, kw 接收的是一个 dict
def printinfo(arg1, *args): print(arg1) print(args, type(args)) printinfo(10) # 仅一个参数, 没有属于args的. # 10 # () <class 'tuple'> printinfo(70, 60, 50) # 除arg1位置匹配的, 其他都传入给可变参数 # 70 # (60, 50) <class 'tuple'>
5.命名关键字参数 ( name keyword argument )
1.命名关键字参数是为了限制调用者可以传入的『 参数名 』,也可以提供 默认值 .
2.与关键字参数不同的是, 关键字参数的名字和值都是任意的, 后续再进行匹配使用, 而命名关键字则只能接受 给定的关键字 作为参数.定义命名关键字参数
3.不要忘了写分隔符 * , 否则定义的是位置参数, 命名关键字参数调用函数时 必须给定参数名 .
def person(name, *, age, height=1.90): print(f'{name}今年{age}岁, 身高{height:.2f}m') person('张三', age=18, height=1.80) # 张三今年18岁, 身高1.80m person('李四', age=18) # 李四今年18岁, 身高1.90m person('王五') # TypeError, 需要传入给定关键字
6.参数组合
在 Python 中定义函数时, 以上这5种参数都可以使用, d但最多可以使用4种, 并且要注意顺序:
1.位置参数、默认参数、可变参数和关键字参数.
2.位置参数、默认参数、命名关键字参数和关键字参数.
变量作用域
在python程序中, 处于不同位置的变量, 有不同的作用域.
定义在函数内部的变量拥有局部作用域, 该变量称为 局部变量 . 定义在函数外部的变量拥有全局作用域, 该变量称为 全局变量 . 局部变量只能在其被声明的函数内部访问, 而全局变量可以在整个程序范围内访问.需要注意的是:
当局部变量试图访问全局变量时, 一定要在函数内声明 global . 当局部变量与全局变量 命名冲突 时, 程序会优先选择局部变量.
内嵌函数和闭包
内嵌函数 就是在外层函数内定义内层函数.
def outer(): print('outer函数在这被调用') def inner(): print('inner函数在这被调用') inner() # 该函数只能在outer函数内部被调用 outer() # outer函数在这被调用 # inner函数在这被调用
闭包 是一个比较重要的语法结构, 结构上与内嵌函数类似, 区别在于返回值, 闭包的外层函数 返回值是一个函数 .
如果在一个内部函数里对外层非全局作用域的变量进行引用, 那么内部函数就被认为是 闭包 .
通过闭包可以访问外层非全局作用域的变量, 这个作用域称为 闭包作用域 .
def funX(x): def funY(y): print('使用funY(y)') return x * y return funY i = funX(8) print(type(i)) # <class 'function'> print(i(5)) # 40
注意到上述代码中, 内部函数 FunY 中使用了外部非全局作用域的变量 x .
同样是, 函数内嵌套的函数作用域也需要特别注意, 若我们需要修改闭包内的变量, 需要使用 nonlocal 关键字.
num = 999 def outer(): num = 10 def inner(): nonlocal num # nonlocal关键字声明 num = 100 print(f'inner中num = {num}') inner() print(f'outer中num = {num}') outer() # inner中num = 100 # outer中num = 100 print(f'全局中num = {num}') # 全局中num = 999
lambda 表达式
lambda需要注意的是:
匿名函数没有 return , 表达式本身就是返回值. 匿名函数拥有『 自己的命名空间 』, 不能访问参数列表之外或全局变量.匿名函数主要适用于函数式编程(函数不会影响函数之外的内容)的一些高阶函数中. 例如map映射和filter过滤, 当然也可以在自己自定义函数中使用.
odd = lambda x: x % 2 == 1 templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9]) print(list(templist)) # [1, 3, 5, 7, 9] m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5]) print(list(m1)) # [1, 4, 9, 16, 25]
面向对象
三大特性
面向对象就必须了解三大特性:
封装: 把客观事物封装成抽象的类, 让数据和方法给信任的类或对象操作, 而隐藏部分信息. 继承: 子类自动共享父类的属性和方法. 一般认为一个类是自身的子类; 可以使用 issubclass(B, A) 查看 B 是否是 A 的子类. python也支持多继承, 但会使得类的整体层次复杂, 所以并 不建议使用 . 多态: 同一个 方法 的调用, 会由于不同对象而得到不同的结果. 必要条件是 继承 和 方法的重写 .
类、类对象 和 实例对象 类: 就是指对类的定义 类对象: 是在创建类的时候, 在内存开辟的一个空间, 一个类只有 一个类对象 . 实例对象: 通过实例化类创建的对象, 可以有多个.
类属性 和 对象属性 类属性: 类定义内, 类方法外定义的变量称为类属性, 类属性属于类对象, 可以被多个实例化对象所 共享 , 就像 我们都有一个家, 名字叫中国 一样. 对象属性: 对象属性和具体创建的对象实例直接相关, 并且相互之间不共享属性, 就像 我的老婆只是我的 一样.
class A(): a = 0 #类属性 def __init__(self, xx): A.a = xx #使用类属性可以通过 (类名.类属性)调用。有一些操作属性的方法:
使用 hasattr(object, name) 来判断对象是否包含对应的 属性或方法 . 使用 getattr(object, name) 来获取 属性或方法 . 使用 setattr(object, name, value) 来修改属性值, 或创建新的属性和值. 使用 delattr(object, name) 来删除属性.class A(object): name = '张三' def set(self, a, b): x = a a = b b = x print(a, b) a = A() print(hasattr(a, 'name')) # 判断是否有name属性 True print(hasattr(a, 'set')) # 判断是否有set方法 True x = getattr(a, 'name') # 获取属性值 print(x) # 张三 c = getattr(a, 'set') # 获取方法 c(a='1', b='2') # 2 1
私有
私有属性和方法仅需在定义命名的时候加上两个下划线" __ "即可.
相对于公有属性和公有方法来说, 私有属性和私有方法更加的安全. 从定义上来说, 将需要安全保护的属性和方法封装为私有, 可以阻止外部直接调用, 而必须使用 实例化对象方法 或 类方法 进行调用, 从而提高安全性.
但在python中的私有是『伪私有』, 即可以使用类名, 通过 object._className__attrName 访问私有属性,用 object._className__func() 访问私有方法.
class JustCounter: __secretCount = 0 # 私有变量 publicCount = 0 # 公开变量 def count(self): self.__secretCount += 1 self.publicCount += 1 print(self.__secretCount) counter = JustCounter() counter.count() # 1 print(counter.publicCount) # 1 # 特殊方法依旧可以访问 print(counter._JustCounter__secretCount) # 1 # 直接访问则会报错. print(counter.__secretCount)实例直接使用 点 就可以 增加属性 了, 这点需要注意一下.
class B: def func(self): print('调用func方法') b = B() print(b.__dict__) # 查看属性 {} b.name = '张三' b.age = 18 print(b.__dict__) # 查看属性{'name': '张三', 'age': 18} b1 = B() print(b1.__dict__) # 查看属性 {}
魔法方法
基本的魔法方法
魔法方法基本上是被下划线包围的一些特殊方法. 相比于普通的方法, 它能够在适当的时候自动调用. 第一个参数一般是 cls 『类方法』或者 self 『实例方法』.
__init__(self[, ...]) 构造器, 当一个实例被创建的时候调用的初始化方法. __new__(cls[, ...]) 在一个对象实例化的时候所调用的第一个方法, 在调用 __init__ 初始化前, 先调用 __new__ . 需要注意的是, __new__ 的返回值必须为当前类的实例, 否则将不会调用 __init__ 初始化. 主要是在继承一些不可变的class(比如 int, str, tuple )时, 提供一个自定义该类实例化过程的途径. __del__(self) 析构器, 当一个对象将要被系统回收之时调用的方法. __str__(self) : 当你打印一个对象、使用 %s 格式化或使用 str 强转数据类型的时候,触发 __str__ . __repr__(self) 是 __str__(self) 的备胎, 情况类似, 不过自定义时往往更加准确, 主要用于调试.
算术运算符
普通的计算在对象中是无法进行的, 需要自定义计算方式.
__add__(self, other) 定义加法的行为: +
__sub__(self, other) 定义减法的行为: -
__mul__(self, other) 定义乘法的行为: *
__truediv__(self, other) 定义真除法的行为: /
__floordiv__(self, other) 定义整数除法的行为: //
__mod__(self, other) 定义取模算法的行为: %
__divmod__(self, other) 定义当被 divmod() 调用时的行为
divmod(a, b) 把除数和余数运算结果结合起来,返回一个包含商和余数的元组 (a // b, a % b) 。
__pow__(self, other[, module]) 定义当被 power() 调用或 ** 运算时的行为
__lshift__(self, other) 定义按位左移位的行为: <<
__rshift__(self, other) 定义按位右移位的行为: >>
__and__(self, other) 定义按位与操作的行为: &
__xor__(self, other) 定义按位异或操作的行为: ^
__or__(self, other) 定义按位或操作的行为: |
还有对应的 反运算符 , 在之前加上 r 即可, 例如 __rsub__ . 对应增量赋值运算符, 在之前加上 i 即可, 例如 __isub__ .
属性访问
__getattr__(self, name) : 定义当用户试图获取一个不存在的属性时的行为.
__getattribute__(self, name) : 定义当该类的属性被访问时的行为(先调用该方法, 查看是否存在该属性, 若不存在, 接着去调用 __getattr__ ).
__setattr__(self, name, value) : 定义当一个属性被设置时的行为.
__delattr__(self, name) : 定义当一个属性被删除时的行为.
描述符
描述符就是将某种特殊类型的类的实例指派给另一个类的属性.
__get__(self, instance, owner) : 用于访问属性, 它返回属性的值.
__set__(self, instance, value) : 将在属性分配操作中调用, 不返回任何内容.
__del__(self, instance) : 控制删除操作, 不返回任何内容.
迭代器和生成器
迭代器
迭代是 Python 最强大的功能之一, 是访问集合元素的一种方式.
迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问, 直到所有的元素被访问完结束. 迭代器只能往前不会后退. 字符串, 列表或元组对象都可用于创建迭代器.迭代器有两个基本的方法: iter() 和 next() :
iter(object) 函数用来生成迭代器. next(iterator[, default]) 返回迭代器的下一个项目. 在元素为空时返回默认值, 若没有则会触发 StopIteration 异常. 在元组推导式和next中使用过, 不过是下面的『生成器』.把一个类作为一个迭代器使用需要在类中实现两个魔法方法 __iter__() 与 __next__() .
__iter__(self) 定义当迭代容器中的元素的行为, 返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成. __next__() 返回下一个迭代器对象. StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。class Fibs: def __init__(self, n=10): self.a = 0 self.b = 1 self.n = n def __iter__(self): return self def __next__(self): self.a, self.b = self.b, self.a + self.b if self.a > self.n: raise StopIteration return self.a fibs = Fibs(100) for each in fibs: print(each, end=' ') # 1 1 2 3 5 8 13 21 34 55 89
生成器
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
跟普通函数不同的是, 生成器是一个返回迭代器的函数, 只能用于迭代操作, 更简单点理解生成器就是一个迭代器. 在调用生成器运行的过程中, 每次遇到 yield 时函数会 暂停并保存 当前所有的运行信息, 返回 yield 的值, 并在下一次执行 next() 方法时从当前位置 继续 运行. 调用一个生成器函数, 返回的是一个迭代器对象.def libs(n): a = 0 b = 1 while True: a, b = b, a + b if a > n: return yield a for each in libs(100): print(each, end=' ') # 1 1 2 3 5 8 13 21 34 55 89
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!
查看更多关于python基础之函数和面向对象详解的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did100045