Fork me on 5号黯区

上传python笔记,看看博客园的编辑器的效果

一、Python 中的文件操作

1.1 文件操作需要了解的前置知识

1.1.1 为什么需要用到文件操作
  • 如果单纯的将程序需要用到的内容直接的保存在内存中,那么程序每一次执行之后,所保存的数据就会消失,这个时候就需要使用某种方式长期的存储数据,例如保存在文件中,或者保存在数据库中。
1.1.2 对路径的划分
  • 相对路径:相对于当前文件所在目录的一个路径。通常使用 . 表示当前目录,.. 表示上级目录,通过 ../../ abc.txt 的指令我们可以找到上上级目录下的 abc.txt 文件
  • 绝对路径:从文件的根目录开始计算的路径。在 windows 下,绝对路径通常是以盘符开头的,在 linux/unix 下绝对路径通常是以 / 开头的,例如 C:\Users\min\Desktop\abc.txt就是一个绝对路径。
  • 如果说一个文件它的位置和当前的项目有关,就可以写作相对路径,如果这个文件和系统有关,就可以写成绝对路径,例如网页设计中的资源我们通常会写作相对路径,用于防止目录的泄露。
1.1.3 二进制文件和文本文件
  • 二进制文件: 指的是拥有特定格式,需要使用对应的算法进行解析的文件,例如可执行文件(.exe)、音频文件(.mp3 .aac)、视频文件(.mp4)、图片文件(.jpg)。
  • 文本文件:其中保存的直接就是字符串,只需要通过基本的文本查看工具就可以打开,不需要专门解析。

1. 2 文件的操作函数

1.2.1 文件的基本操作函数

看看https://zhuanlan.zhihu.com/p/112630994

  • open: 用于打开文件,将文件和文件对象建立关联。其中第一个参数是文件的路径,第二个参数是文件的打开方式,还有一个参数 encoding 用于说明文件的编码类型。

    # 通过 open 函数可以将文件对象和一个文件建立关联
    # 文件的打开方式通常有 r(读) w(写) a(追加) b(在操作二进制文件的时候使用)
    # 函数的返回值是一个文件对象,通过文件对象可以执行相应的文件操作
    file = open(r'data.txt', 'w+')
    
    # 通过 open 函数可以将文件对象和一个文件建立关联
    # 文件的打开方式通常有 r(读) w(写) a(追加) b(在操作二进制文件的时候使用)
    # 函数的返回值是一个文件对象,通过文件对象可以执行相应的文件操作
    try:
        # 使用这个写法会自动的关闭文件,不需要编写 file.close()
        with open(r'data.txt', 'w+') as file:
            file.write('hello world')
    except Exception as e:
        print(e)
    
  • file.close(): 用于关闭一个文件,否则可能产生问题

    # 通常在文件操作完毕之后,需要关闭文件,没有正确的关闭文件可能会导致两个问
    # 题,(1) 文件被占用,在当前项目中无法再次打开文件。(2) 文件的修改无法被应用
    file.close()
    
  • file.write() \ file.writelines(): 向文件内写入数据

    # 通过 write 一次只能写入一个字符串
    file.write('hello world')
    # 通过 writelines 一次可以写入多个字符串
    file.writelines(['hello', ' world'])
    
  • file.read() \ file.readlline() \ file.readlines(): 从文件内读取数据

    # 读取所有内容,可以传入一个参数,表示想要读取的字符数
    print(file.read())
    # 读取一行内容,可以传入一个参数,表示想要读取的字符数
    print(file.readline())
    # 读取文本内的所有内容,将每一行作为一个元素保存到列表中
    print(file.readlines())
    
1.2.2 文件指针操作函数
  • 文件指针是什么? 文件指针用于标识当前文件的读写位置,通过移动文件指针可以改变读写的起始位置

  • seek: 设置文件的读写位置。

    # 通过 seek 将文件指针移动到倒数第 4 个位置
    # 0: 文件开头开始算起,1:当前位置,2: 文件末尾。
    file.seek(-4, 2)
    print(file.read())
    
  • tell: 获取文件指针当前所在的位置。

    # tell 函数用于获取当前文件指针的位置,可以计算文件大小
    file.seek(0, 2)
    print(file.tell())
    
1.2.3 通过 os 模块操作文件
  • 需要使用 import os 导入 os 模块,使用其中提供的文件操作函数来操作文件
  • python 的内置文件操作使用的是面向对象的编程方式file.read(),而 os 模块使用面向过程的方式 read(file)
file = os.open('data.txt', os.O_RDONLY)
print(os.read(file, 2))
os.lseek(file, 5, os.SEEK_SET)
print(os.read(file, 2))
os.close(file)

二、面向对象编程

2.1 面向对象和面向过程

2.1.1 面向过程
  • 思想:程序设计的着眼点在于处理问题的一系列过程(函数)。
  • 特点:数据和处理数据的函数 分离的。
2.1.2 面向对象
  • 思想:程序设计的着眼点在于解决这个问题所需要的对象有哪些,需要完成什么功能。

  • 特点:封装特性,将数据和处理数据的函数封装到了一起。

  • 三大特性:封装 继承 多态

2.2 类的编写

2.2.1 简单的类
  • 通过 class 定义出一个类,一个类其实就是一个自定义的类型,其中描述了某个类别的对象可以实现的功能(方法)和一些描述信息(属性),通过 对象 = 类名() 的方式可以创建出一个对象。
# 简单类的定义方式,使用关键字 class
class ClassName(object):
    # 类成员函数,在 python 中被称为方法
    def class_func1(self):
        print('def class_func(self);')
    # 类成员函数,第一个参数必须是 self,后续可以含有其它的参数
    def class_func2(self, value = 100):
        print('def class_func(self, %d);' % value)
# 通过对象名 = 类名(参数) 的方式可以创建一个对象
object1 = ClassName()
object1.class_func1()
object1.class_func2()
2.2.2 构造函数和析构函数
  • 构造函数:python 中的每一个类中都拥有一个构造函数叫做 __init__,这个函数会被用于执行初始化操作,在一个对象被创建时,自动的调用一次。

  • 析构函数: python 中的每一个类中都拥有一个析构函数叫做 __del__,这个函数会被用于执行清理操作,在对象被删除的时候,自动的调用一次

    # 一个包含构造函数和析构函数的类
    class ClassName2(object):
    
        # 当前类的构造函数,函数可以拥有参数,在创建对象是传入
        def __init__(self, name, age):
            # 在构造函数内执行基本的初始化操作
            print("def __init__(self, %s, %d)" % (name, age))
    
        # 析构函数用于释放资源,但是由于 python 存在垃圾回收机制
        # 所以析构函数使用的比较少,我们可以在其中关闭文件等资源
        def __del__(self):
            # 输出表示析构函数被调用了
            print("def __del__(self)")
    
    # 创建对象时需要传入参数,和构造函数的参数对应,因为创建对象时,会自动调用__init__()函数,当然也会执行里面的语句,在这里是一个print()函数打印相应的内容
    object2 = ClassName2('xiaoming', 18)
    >>>def __init__(self, xiaoming, 18)
    def __del__(self)
    object3 = ClassName2('xiaohong', 80)
    >>>def __init__(self, xiaohong, 80)
    def __del__(self)                                  
    

关于析构函数何时被调用:

析构方法__del__是对象在被垃圾回收的时候起作用的一个方法,它的执行一般也就意味着对象不能够继续引用, 回收内存.
内存回收的方式有两种:

  • 1、当对象在某个作用域中调用完毕,在跳出其作用域的同时析构函数会被调用一次,这样可以用来释放内存空间:
#!/usr/bin/env python
#-*- coding: utf-8 -*-
class Foo:
    #初始化方法
    #创建完对象后会自动被调用
    def __init__(self):
        print('构造方法被调用')
    #析构方法
    #当对象被删除时,会自动被调用,然后释放内存
    def __del__(self):
        print('脚本运行结束, 释放内存')
#创建对象
f = Foo()
print('这应该是脚本最后一句.')
# 这个地方整个文件的作用域就结束了,所以对象f结束其生命周期(因为程序运行停止而结束),系统自动执行析构函数来做清理善后的工作,所以打印"脚本运行结束, 释放内存"成了最后执行的语句.
运行结果:
    构造方法被调用
	这应该是脚本最后一句.
	脚本运行结束, 释放内存

Process finished with exit code 0
  • 2、使用del 删除对象时,会调用他本身的析构函数, 相当于手动释放内存
class Foo:
    #初始化方法
    #创建完对象后会自动被调用
    def __init__(self):
        print('构造方法被调用')

    #析构方法
    #当对象被删除时,会自动被调用,然后释放内存
    def __del__(self):
        print('脚本运行结束, 释放内存')

#创建对象
f = Foo()
#删除对象
del f # 删除就立马调用析构函数,(因为del删除对象而结束,对象结束,系统就会调用析构函数)
print('这应该是脚本最后一句.')
运行结果:
	构造方法被调用
	脚本运行结束, 释放内存
	这应该是脚本最后一句.

Process finished with exit code 0
2.2.3 实例属性和类属性
  • 实例属性: 具体的某一个对象拥有的属性

  • 通过 self 和 实例名称都可以添加实例属性,添加的属性是归某一个具体的实例所有的。构造函数中的一开始初始化的实例属性会被所有的实例拥有(那是因为所有的实例都会调用构造函数)

    # 一个含有实例属性的类
    class ClassName3(object):
    
        # 构造函数,用于初始化实例属性
        def __init__(self, name, age):
            # 1. 为当前的实例(对象)创建两个个实例属性并进行初始化
            self.age = age
            self.name = name
    
        # 一个方法,调用之后可以添加实例属性
        def add_value(self, v):
            # 2. 通过 self 可以在方法内为调用当前方法的实例添加实例属性,在类外面添加属性:实例名称.属性 = value 进行赋值添加
            self.value = v
    
        # 用于输出当前类内的所有属性
        def print_member(self):
            # __dict__可以输出当前实例内的属性组成的键值对
            print(self.__dict__)
    
    object4 = ClassName3('xiaoming', 80)
    object4.print_member()
    >>>{'age': 80, 'name': 'xiaoming'}
    object5 = ClassName3('xiaohong', 18)
    object5.add_value(30)
    object5.print_member()
    >>>{'age': 18, 'name': 'xiaohong', 'value': 30}
    # 3. 通过实例名称直接的点出属性进行赋值添加
    object4.value2 = 300
    object4.print_member()
    >>>{'age': 80, 'name': 'xiaoming', 'value2': 300}
    
  • 类属性:归整个类所有,被所有的对象共有的属性

  • 通过类名和在类内直接定义可以添加类属性,添加的属性是归所有的实例拥有的

    # 一个含有类属性的类
    class ClassName4(object):
    
        # 1. 在类内的一级缩进内直接定义的属性就是类属性
        class_value = 10
    
        # 构造函数,用于访问类属性
        def __init__(self):
            # 在构造函数中可以使用下面的两种形式访问类属性
            print(self.class_value)
            print(ClassName4.class_value)
    
        # 一个方法,调用之后可以添加类属性
        def add_value(self, v):
            # 2. 通过 类名 可以在方法内为整个类添加一个类属性 3. 在类外也可以通过类名直接的添加属性
            ClassName4.value = v
    
        # 用于输出当前类内的所有属性
        def print_member(self):
            # 实例.__dict__ 保存的是实例内的所有属性
            # 类名.__dict__ 保存的是类内的所有属性
            print('实例', self.__dict__)     # 因为没有实例属性,所以结果是{}
            print('类', ClassName4.__dict__)
    
    object5 = ClassName4()
    object5.print_member()
    >>>
    10
    10
    实例 {}
    类 {'__module__': '__main__', 'class_value': 10, '__init__': <function ClassName4.__init__ at 0x0000027B3F2414C0>, 'add_value': <function ClassName4.add_value at 0x0000027B3F241550>, 'print_member': <function ClassName4.print_member at 0x0000027B3F2415E0>, '__dict__': <attribute '__dict__' of 'ClassName4' objects>, '__weakref__': <attribute '__weakref__' of 'ClassName4' objects>, '__doc__': None}
    # 3. 在类外可以通过类名直接的添加属性
    ClassName4.value2 = 1000
    print(object5.value2)  # 在类外没有这种东西:self.value3 = 2000, self只能在类里面使用
    >>>1000
    object5.print_member()
    >>>
    实例 {}
    类 {'__module__': '__main__', 'class_value': 10, '__init__': <function ClassName4.__init__ at 0x000001EA731D14C0>, 'add_value': <function ClassName4.add_value at 0x000001EA731D1550>, 'print_member': <function ClassName4.print_member at 0x000001EA731D15E0>, '__dict__': <attribute '__dict__' of 'ClassName4' objects>, '__weakref__': <attribute '__weakref__' of 'ClassName4' objects>, '__doc__': None, 'value2': 1000}
    
  • 类属性的访问方式

    # 类属性的访问方式(接上面一段代码的内容)
    object6 = ClassName4()
    >>>
    10
    10
    object6.print_member()
    >>>
    实例 {}
    类 {'__module__': '__main__', 'class_value': 10, '__init__': <function ClassName4.__init__ at 0x00000259999D14C0>, 'add_value': <function ClassName4.add_value at 0x00000259999D1550>, 'print_member': <function ClassName4.print_member at 0x00000259999D15E0>, '__dict__': <attribute '__dict__' of 'ClassName4' objects>, '__weakref__': <attribute '__weakref__' of 'ClassName4' objects>, '__doc__': None}
    # 可以使用 类名 或 实例名 直接点出属性进行访问类
    print(object6.class_value)
    >>>10
    print(ClassName4.class_value)
    >>10
    
    # 不能通过实例.类属性的方式修改属性,这样的操作会创建出一个和类属性同名的实例属性,不管是修改还是访问类属性都推荐使用类名进行操作
    object6.class_value = 1515
    object6.print_member()
    >>>
    实例 {'class_value': 1515}# 果然创建出一个和类属性同名的实例属性
    类 {'__module__': '__main__', 'class_value': 10, '__init__': <function ClassName4.__init__ at 0x00000261AC2314C0>, 'add_value': <function ClassName4.add_value at 0x00000261AC231550>, 'print_member': <function ClassName4.print_member at 0x00000261AC2315E0>, '__dict__': <attribute '__dict__' of 'ClassName4' objects>, '__weakref__': <attribute '__weakref__' of 'ClassName4' objects>, '__doc__': None}
    
    2.3 self 的作用
    • self 用于标识当前函数是被谁调用的,self实际上就是对象的地址 id(object) ,可以进行访问
  # self 的作用
class ClassName5(object):
  
      # 构造函数,用于初始化实例属性
      def __init__(self, value):
          self.value = value
  
      # 访问实例属性的方法
    def visit_value(self):
          print(hex(id(self)), self.value)
  
  # 对于每一个实例(对象)来说,函数使用的都是相同的,但是实例属性归属于每一个实例自己,函数是通过self区分当前需要用到的数据到底是属于谁的
  
  object7 = ClassName5(100)
    object8 = ClassName5(200)
    object7.visit_value()
    >>>0x1bb49968400 100
    object8.visit_value()
    >>>0x1bb49a52760 200
   
    # 上面的函数调用可以看作 ClassName5(id(object8), 参数)
    print(hex(id(object7)), type(object7))
    >>>0x229bead8400 <class '__main__.ClassName5'>
    print(hex(id(object8)), type(object8))
    >>>0x229bebc1760 <class '__main__.ClassName5'>
  

1、self是什么
在python的类中self代表实例本身,具体来说,是该实例的内存地址。
在调用实例的方法时,Python解释器会自己把实例!!变量!!传给类的函数中的self。
以上述代码I为例,代码I定义了一个类Test,在这个类中,self为参数变量,在类Test实例化得到实例ins时,python解释器自动调用__init__,执行Test.init(ins, 123),该self可接收实例ins的内存地址,从而self代表了实例本身。类似的,如果实例化ins后,执行ins.fun1( ),python解释器会将ins.fun1( )解释成Test.fun1(ins)。可见,self这个变量是无需用户手动传送值的,解释器会自动帮我们给其传递实例。
需要注意的是,self不是关键字,换言之,可以用其它的合法变量名替换self,但是,规范和标准建议我们一致使用self。

2、self的使用场景
在类中,self的使用有下面3个场景:
1)self为类中的函数的第一个参数,例如在类中,def fun1(self, …)。
上文说过,“在调用实例的方法时,Python解释器会自己把实例变量传给类的函数中的self”,如果类的函数的第一个参数不是代表实例的self,则调用实例的方法时,该方法没有参数接收解释器自动传入的实例变量,从而程序会产生异常。
事实上,“和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数”(廖雪峰老师说的)。

​ 2)在类中,引用实例的属性,示例:self.变量名(如self.val0)。
引用实例的属性的目的是为实例绑定属性、写入或读取实例的属性。
例如,在代码I中,在类的函数__ init __中,“self.val1 = val1”将属性val0绑定到了实例self(类实例化成ins后,self就代表实例ins了)上,并且将变量val1的值赋给了实例的属性val0。在函数fun1中,print(self.val0),读取了实例self的值val0,并打印出来,当然,在函数中修改属性val0的值也是可以的。

​ 3)在类中,调用实例的方法,例如,self.fun1();获取实例的方法的地址,例如self.fun1。
类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。既然,self代表实例,则可以“self.函数名”的方式表示实例的方法地址,以“self.函数名()”的方式,调用实例的方法。在类的定义中,以及实例化后对实例方法的调用,都可以这样做。

3、python的几种变量——按作用域分
a、全局变量:在模块内、在所有函数外面、在class外面,这就是全局变量。
b、局部变量:在函数内、在class的方法内(未加self修饰的) ,这就是局部变量
c、静态变量(也可以说,类属性):在class内的,但不在class的方法内的,这就是静态变量
d、实例变量(也可以说,实例属性):在class的方法内的,用self修饰的变量,这就是实例变量

4、self和变量的关系
综合上述的1、2和3点,可以得到在类中,self和变量的关系了,一言以蔽之,被self修饰的变量是实例变量,不被self修饰的变量不是实例变量。
实例变量有什么作用,或者说,什么时候应该使用self修饰变量比较好?我的总结如下:
当我们想将某个变量绑定给实例时,就在类中,使用self修饰该变量。一般来说,类实例化为不同实例后,为了不同实例的某一变量互不干扰,就将该变量绑定给实例。

2.4 访问属性控制
2.4.1 单下划线:文件保护变量
# 当一个模块内的变量以单下划线开头,则表示这个变量不希望被其它文件访问,以 from m import * 的方式就访问不到这个变量,其它的变量可以照常访问
from module import *
# print(_protected_value)
print(normal_value)

# 通过 import 和 from m import v 的方式都可以照常访问
import module
print(module._protected_value)

from module import _protected_value
print(_protected_value)
2.4.2 双下划线:私有成员
  # 通过代码来了解python 中的保护属性
  class ClassName6(object):
      # 以双下划线开头的是私有属性,不能被外界直接访问
      __value = 12341515
  
  # 私有属性实际就是被解释器改头换面换了名字,具体的格式如下: _ + 类名 + 双下划线开头的属性名,写成这样任然可以访问
  # 改变类的私有变量的值有2种方法:
  # 	间接:为这个私有变量提供一个操作的方法,如:def get_score(self, score)
  # 	直接:实例名._类名__私有变量名 = 值, 如:f._Student__score = 10

  print(ClassName6._ClassName6__value)
  >>>12341515
  # 在 python 中,单下划线开头的属性约定俗称通常是不需要
  # 被外界访问的,在写代码或使用第三方库的时候,不要修改
  # 和而访问其它模块中的属性

2.3魔术方法

__complex__(self)	定义当被 complex() 调用时的行为(需要返回恰当的值)
__int__(self)	定义当被 int() 调用时的行为(需要返回恰当的值)
__float__(self)	定义当被 float() 调用时的行为(需要返回恰当的值)
__round__(self[, n])	定义当被 round() 调用时的行为(需要返回恰当的值)
__index__(self)	1. 当对象是被应用在切片表达式中时,实现整形强制转换
	2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 __index__
  3. 如果 __index__ 被定义,则 __int__ 也需要被定义,且返回相同的值
	上下文管理(with 语句)
__enter__(self)	1. 定义当使用 with 语句时的初始化行为
	2. __enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定
__exit__(self, exc_type, exc_value, traceback)	1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么
	2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作
 容器类型	
__len__(self)	定义当被 len() 调用时的行为(返回容器中元素的个数)
__getitem__(self, key)	定义获取容器中指定元素的行为,相当于 self[key]
__setitem__(self, key, value)	定义设置容器中指定元素的行为,相当于 self[key] = value
__delitem__(self, key)	定义删除容器中指定元素的行为,相当于 del self[key]
__iter__(self)	定义当迭代容器中的元素的行为
__reversed__(self)	定义当被 reversed() 调用时的行为
__contains__(self, item)	定义当使用成员测试运算符(in 或 not in)时的行为
posted @ 2020-07-04 15:25  小萝卜特·唐尼  阅读(93)  评论(0编辑  收藏  举报