1、fn是函数对象,fn()是调用函数。所有的函数都是可调用对象。一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法__call__()。
2、如果形参执行的是一个对象(如a),当我们通过形参去修改对象时,会影响到所有指向该对象的变量。
解决方法:a.copy()或a[:]
3、在定义函数时,可以在形参前边加上一个*,这样这个形参将会获得到所有的实参,它将会将所有的实参保存到一个元组中,如a,b,*c = (1,2,3,4,5,6),则a=1,b=2,c=(3,4,5,6),如果*在中间,则需要使用关键字参数的形式传递。
如果在形参的开头直接写一个*,则要求我们的所有的参数必须以关键字参数的形式传递。
*形参只能接受位置参数,而不能接受关键字参数
**形参可以接受其他的关键字参数,它会将这些参数统一保存到一个字典中,**形参只能有一个,并且必须写在所有参数的最后
传递实参时,也可以在序列类型的参数前添加*,这样他会自动将序列中的元素依次作为参数传递,要求序列中元素的个数必须和形参个数一致(如t=(10,20,30),fn(*t))
通过**来对一个字典进行解包操作
4、help(function)可以查看function函数功能,可通过def fn(a:int,b:bool,c:str)对变量进行描述,这并不代表a必须是int类型,仅仅是一个描述。
5、通过locals()可查看当前命名空间
6、sorted()和sort()用法基本一致,使用sorted()可以对任意的序列进行排序,并且使用sorted()排序不会影响原来的对象,而是返回一个新对象
7、形成闭包的条件:
①函数嵌套
②将内部函数作为返回值返回
③内部函数必须要使用到外部函数的变量
8、如果我们直接通过修改函数的代码来完成函数打印的需求,会产生以下问题:
①如果要修改的函数过多,修改起来会很麻烦
②不方便后期的维护
③违反开闭原则(OCP)
我们希望在不修改原函数的基础上,对函数进行扩展,我们可以采用装饰器,在不修改原来函数的情况下来对函数进行扩展,在开发中,都是通过装饰器来扩展函数功能。在定义函数时,可以通过@装饰器来使用指定的装饰器,来装饰当前的函数,同时可以为一个函数指定多个装饰器。
9、封装是面向对象三大特性之一,封装指的是隐藏对象中一些不希望被外部所访问到的属性和方法
如何隐藏一个对象中的属性?
- 将对象的属性名,修改为一个外部不知道的名字
如何获取(修改)对象中的属性?
- 需要提供一个getter和setter方法使外部可以访问到属性
- getter获取对象中的指定属性(get_属性名)
- setter用来设置对象的指定属性(set_属性名)
使用封装,确实增加了类的定义复杂程度,但是它也确保了数据的安全性
- 隐藏了属性名,使调用者无法随意的修改对象中的属性
- 增加了getter和setter方法,很好的控制了属性是否是只读的
如果希望属性是只读的,则可以直接去掉setter
如果希望属性不能被外部访问,则可以直接去掉getter
- 使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
- 使用getter方法获取属性,使用setter方法设置属性
可以在读取属性和修改属性的同时做一些其他的处理
10、可以为对象的属性使用双下划线开头,__xxx
双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
其实隐藏属性只不过是Python自动为属性改了一个名字
实际上是将名字修改为了_类名__属性名,比如__name -> _Person__name
使用__开头的属性,实际上依然可以在外部访问,所以这种方式一般不用
一般我们会将一些私有属性(不希望被外部访问的属性)以_开头
一般情况下,使用_开头的属性都是私有属性,没有特殊需要不要修改私有属性
11、property装饰器,用来将一个get方法,转换为对象的属性
添加为property装饰器以后,我们就可以像调用属性一样使用get方法
使用property装饰的方法,必须和属性名一样
setter方法的装饰器:@属性名.setter
12、继承是面向对象三大特性之一
通过继承我们可以使一个类获取到其他类中的属性和方法
在定义类时,可以在类名后的括号中指定当前类的父类(超类,基类,super)
子类(衍生类)可以直接继承父类中的所有属性和方法
通过继承可以直接让子类获取到父类的方法或属性,避免编写重复性的代码,并且也符合OCP原则,所以需要通过继承来对一个类进行扩展
13、在创建类时,如果省略了父类,则默认父类为object,object是所有类的父类,所有类都继承自object。
父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法。希望可以直接调用父类的__init__来初始化父类中定义的属性,super()可以用来获取当前类的父类,并且通过super()返回对象调用父类方法时,不需要传递self。
14、类名.__base__这个属性可以用来获取当前类的所有父类
在Python中是支持多重继承的,也就是可以为一个类同时指定多个父类
可以在类名的()后面添加多个类,来实现多重继承
多重继承,会使子类同时拥有多个父类,并且会获得到所有父类中的方法
在开发中没有特殊情况,应该避免使用多重继承,因为多重继承会让代码过于复杂
如果多个父类中有同名的方法,则会从第一个父类中寻找,然后找第二个,然后找第三个。。。前面会覆盖后面的
15、多态是面向对象的三大特征之一,多态从字面上理解是多种形态。一个对象可以以不同的形态去呈现。如果一个函数只适用于一个类,其他类的对象都无法使用该函数,这个函数就违反了多态,这样导致函数的适应性非常地差。
16、面向对象的三大特征
- 封装
- 确保对象中的数据安全
- 继承
- 保证了对象的可拓展性
- 多态
- 保证了程序的灵活性
17、类属性,直接在类中定义的属性是类属性,类属性可以通过类或类的实例访问到,但是类属性只能通过类对象来修改,无法通过实例对象修改。
实例属性,通过实例对象添加的属性属于实例属性,实例属性只能通过实例对象来访问和修改,类对象无法访问修改
-
实例方法
- 在类中的定义,以self为第一个参数方法都是实例方法
- 实例方法在调用时,Python会将调用对象作为self传入
- 实例方法可以通过实例和类去调用
- 当通过实例调用时,会自动就当前调用对象作为self传入
- 当通过类调用时,不会自动传递self,此时我们必须手动传递self
-
类方法
-
类方法 在类内部使用@classmethod来修饰的方法属于类方法
-
类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象
-
类方法和实例方法的区别,实例方法的第一个参数是self,而类方法的第一个参数是cls
-
类方法可以通过类去调用,也可以通过实例调用,没有区别
-
-
-
静态方法
-
在类中使用@staticmethod来修饰的方法属于静态方法
-
静态方法不需要指定任何的默认参数,可以通过类和实例调用
-
静态方法基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
-
静态方法一般都是一些工具方法,和当前类无关
-
18、垃圾回收
程序运行过程中,产生的垃圾会影响到程序的运行的运行性能,所以这些垃圾必须被及时清理
在程序中没有被引用的对象就是垃圾,这种垃圾对象过多以后会影响到程序的运行的性能
因此需要近视的垃圾回收,所谓的垃圾回收就是将垃圾对象从内存中删除
在Python中有自动的垃圾回收机制,它会自动将这些没有被引用的对象删除
19、特殊方法(魔法方法)
特殊方法都是使用__开头和结尾的,特殊方法一般不需要我们手动调用,需要在一些特殊情况下自动执行
20、当我们模块中代码过多,或者一个模块需要被分解为多个模块,这时就需要使用到包
普通的模块就是一个py文件,而包是一个文件夹
包中必须要包含一个__init__.py这个文件,这个文件中可以包含包中的主要内容
__pycache__是模块的缓存文件
py代码在执行前,需要被解析器先转换为机器码,然后再执行
因此在使用模块时,也需要将模块的代码先转换为机器码然后再交由计算机执行
而为了提高程序运行的性能,python会在编译过一次以后,将代码保存到一个缓存文件中
这样在下次加载这个模块时,就可以不再重新编译而是直接加载缓存中编译好的代码即可
21、程序运行过程中,一旦出现异常将会导致程序立即终止,异常以后的代码全部都不会执行
处理异常
- 程序运行时出现异常,目的并不是让我们的程序终止
Python是希望在出现异常时,我们可以编写代码来对异常进行处理
try必须有,else可以没有,except/finally必须要有一个try: 代码块(可能出现错误的语句) except 异常类型 as 异常名: 代码块(出现错误以后的处理方式) except 异常类型 as 异常名: 代码块(出现错误以后的处理方式) else: 代码块(没出错时要执行的语句) finally: 代码块(该代码总会执行)
22、文件的读取
通过open()来打开一个文件,可以将文件分为两种类型
一种是纯文本文件(使用utf-8等编码编写的文本文件),一种是二进制文件(图片、MP3、ppt等文件)
open()打开文件时,默认是以文本文件的形式打开的,但是open()默认的编码为None,因此处理文本文件时,必须要指定文件的编码
with open(file_name, encoding=‘utf-8’) as file_obj:
- 通过read()来读取文件中的内容
- 如果直接调用read()会将文本文件的所有内容全部都读取出来
- 如果要读取的文件较大的话,会一次性将文件的内容加载到内存中,容易导致内存泄露
因此对于较大的文件,不要直接调用read()
read()可以接受一个size作为参数,该参数用来指定要读取的字符的数量,默认值为-1,它会读取文件中的所有字符,可以为size指定一个值,这样read()会读取指定数量的字符。每一次读取都是从上次读取到位置开始读取的,如果已经读到了文件的最后,则会返回" "空串,readline()用于一行一行读取内容,它会一次性将读取到的内容封装到一个列表中返回
- tell()方法用来查看当前读取的位置
- seek()可以修改当前读取的位置,需要两个参数
- 第一个参数,是要切换到的位置
- 第二个参数,计算位置方式
- 可选值:
- 0 从头开始,默认值
- 1 从当前位置计算
- 2 从最后位置开始计算
23、文件的写入
使用open()打开文件时必须要指定打开文件所要做的操作(读、写、追加)
如果不指定操作类型,则默认是读取文件,而读取文件时是不能向文件中写入的
- ‘r’表示只读;
- ‘w’表示是可读的,使用w写入文件时,如果文件不存在会创建文件,如果文件存在则会截断文件(即删除原来文件中的所有内容)
- ‘a’表示追加内容,如果文件不存在会创建文件,如果文件存在则会向文件中追加内容
- +为操作符增加功能
- ‘x’用来新建文件夹,如果不存在则创建,存在则报错
- ‘r+’既可读又可写。
读取模式
- ‘t’读取文本文件(默认值)
- ‘b’读取二进制文件
write()可以向文件中写入内容
如果操作的是一个文本文件的话,则write()需要传递一个字符串作为参数
该方法可以分多次向文件中写入内容,写入完成以后,该方法会返回写入的字符的个数