Python基础(二)完结

今天分享又来了呀。ღ( ´・ᴗ・` ) 一起学习进步ღゝ◡╹)ノ♡

关注公众号,回复“资料全集”,不定期最新大数据业内资讯。

❤:在这里跟我一起学习技术、职场、人生、原理、健身、摄影、生活等知识吧!

❤: 欢迎点个关注一起学习,进步充实人生。

摘要:

1. 类与面向对象的经典代码示例。

2. 异常与文件写入。

在开发系统中,面向对象编程,使得代码更简洁、逻辑更清晰;读取配置文件,实现自动化同步数据需要文件读取与写入。

python 基础分为上下两部分,今天为下半部,通过Python基础教程能够看懂绝大多数Python代码。

加油,好好学习,天天向上~ 

Python基础(二)

第六章 对象(Object)

什么是对象?

- 对象是内存中专门用来存储数据的一块区域。数据要在内存里存着,要有一定的规律,不能瞎放。
- 对象中可以存放各种数据(比如:数字、布尔值、代码)。**可以简单理解对象就是一个盒子,一个容器,可以往里面存放各种东西。**
- 对象由三部分组成:
    1.对象的标识(id)
    2.对象的类型(type)
    3.对象的值(value)

面向对象(oop)

- Python是一门面向对象的编程语言
- 所谓的面向对象的语言,简单理解就是语言中的所有操作都是通过对象来进行的。**所以第一件事就是先找到对象**。
- 面向过程的编程的语言
    - 面向过程指将我们的程序的逻辑分解为一个一个的步骤,
        通过对每个步骤的抽象,来完成程序
    - 例子:
        - 孩子上学(下面都是步骤)
            1.妈妈起床
            2.妈妈上厕所
            3.妈妈洗漱
            4.妈妈做早饭
            5.妈妈叫孩子起床
            6.孩子上厕所
            7.孩子要洗漱
            8.孩子吃饭
            9.孩子背着书包上学校

    - 面向过程的编程思想将一个功能分解为一个一个小的步骤,
        我们通过完成一个一个的小的步骤来完成一个程序
    - 这种编程方式,符合我们人类的思维,编写起来相对比较简单
    - 但是这种方式编写代码的往往只适用于一个功能,
        如果要在实现别的功能,即使功能相差极小,也往往要重新编写代码,
        所以它可复用性比较低,并且难于维护。
                如果把代码封装进函数,也只是增加了复用性。

- 面向对象的编程语言
    - 面向对象的编程语言,**关注的是对象,而不关注过程**
    - 对于面向对象的语言来说,一切都是对象
    - 例子:
        1.孩他妈起床叫孩子上学  **(妈妈要做的步骤我们不关注,她会自动去解决,这就是面向对象,只关注结果本身)**    妈妈具体怎么做的,是**由这个对象里面的数据**去决定。

    - 面向对象的编程思想,将所有的功能、步骤统一**保存到**对应的对象中(**所以面向对象是包含面向过程的**)
        比如,妈妈功能保存到妈妈的对象中,孩子的功能保存到孩子对象中
        要使用某个功能,直接找到对应的对象即可
    - 这种方式编写的代码,比较容易阅读,读起来比较清晰,并且比较易于维护,容易复用。—— 不适合开发太复杂的程序,但这个是基础
    - 但是这种方式编写,不太符合常规的思维,每个对象里要有不同的功能,编写起来稍微麻烦一点 

- 简单归纳一下,面向对象的思想
    **1.找对象**(就是要用功能,就先去找对应的对象,比如说我要保存一组数据,那就去找序列;想存键值对的数据,那就先找字典)
    **2.搞对象**(找到对象了,但是想要对象给你办事啊)

类(class)

- 我们目前所学习的对象都是Python内置的对象
- 但是内置对象并不能满足所有的需求,所以我们在开发中经常需要自定义一些对象。我们要怎么自定义对象?
- **类,简单理解它就相当于一个图纸。在程序中我们需要根据类来创建对象**
- 类就是对象的图纸!
- 我们也称对象是类的实例(instance)
- 如果多个对象是通过一个类创建的,我们称这些对象是一类对象
- 像 int() float() bool() str() list() dict() .... 这些都是类
- a = int(10) # 创建一个int类的实例 等价于 a = 10
- 我们**自定义的类都需要使用大写字母开头**,使用大驼峰命名法(帕斯卡命名法)来对类命名

- 类也是一个对象!图纸也是一个对象。
- 类就是一个用来创建对象的对象!
- 类是type类型的对象,定义类实际上就是定义了一个type类型的对象

01类的简介.py

a = int(10) # 创建一个int类的实例
b = str('hello') # 创建一个str类的实例

print(a , type(a)) # 10 <class 'int'>
print(b , type(b)) # hello <class 'str'>

# 定义一个简单的类
# 使用class关键字来定义类,语法和函数很像!
# class 类名([父类]):
#   代码块
class MyClass():
    pass

print(MyClass) # <class '__main__.MyClass'>   
含义:class表示这是一个类。'__main__'表示当前文件是我们的主文件。.MyClass表示主文件里面的类叫Myclass

# 使用MyClass创建一个对象
**# 使用类来创建对象,就像调用一个函数一样**
mc = MyClass() # 通过MyClass创建的对象,然后返回给变量mc。mc是MyClass的实例
mc_2 = MyClass()
mc_3 = MyClass()
mc_4 = MyClass()
# mc mc_2 mc_3 mc_4 都是MyClass的实例,他们都是一类对象
# **isinstance()用来检查一个对象是否是一个类的实例**
result = isinstance(mc_2,MyClass)
# result = isinstance(mc_2,str)

# print(mc , type(mc))  # <__main__.MyClass object at 0x000002114760AA20> <class '__main__.MyClass'>
print('result =',result) # result = True

# 对象都有id、type、value
print(id(MyClass) , type(MyClass))  # 3113387433992 <class 'type'>

# 现在我们通过MyClass这个类创建的对象都是一个空对象
# 也就是对象中实际上什么都没有,就相当于是**一个空的盒子(既然是空盒子,就可以往盒子里添加东西)**
# **可以向对象中添加变量,对象中的变量称为属性。**
# 语法:对象.属性名 = 属性值

mc.name = '孙悟空'
mc_2.name = '猪八戒'  

print(mc_2.name)

使用类创建对象的流程(参考图1)

简化版步骤:
1.创建一个变量
2.在内存中创建一个新对象
3.将对象的id赋值给变量

MyClass 也相当于一个变量。相当于有两个作用,一个是变量、一个是类名。

类的定义(参考图2)

- 类和对象都是对现实生活中的事物或程序中的内容的抽象(不能凭空创建,凭空创建没有意义)

**- 实际上所有的事物都由两部分构成:
    1.数据(属性)
    2.行为(方法)**

- 在类的代码块中,我们可以定义变量和函数,
    **变量**会成为该类实例的**公共属性**,所有的该类实例都可以通过 对象.属性名 的形式访问
    **函数**会成为该类实例的**公共方法**,所有该类实例都可以通过 对象.方法名() 的形式调用方法

- 注意:
    方法调用时,第一个参数由解析器自动传递,所以定义方法时,至少要定义一个形参!

- 实例为什么能访问到类中的属性和方法?
    类中定义的属性和方法都是公共的,任何该类实例都可以访问。

    - 属性和方法查找的流程
        当我们调用一个对象的属性时,解析器会先在当前对象中寻找是否含有该属性,
            如果有,则直接返回当前的对象的属性值,
            如果没有,则去当前对象的类对象中去寻找,如果有则返回类对象的属性值,
            如果类对象中依然没有,则报错!

    - 类对象和实例对象中都可以保存属性(方法)
        - 如果这个属性(方法)是所有的实例共享的,则应该将其保存到类对象中
        - 如果这个属性(方法)是某个实例独有,则应该保存到实例对象中

    - 一般情况下,属性保存到实例对象中
        而方法需要保存到类对象中

当类对 对象没有约束,那这个类就没有意义了,失去了图纸的功能。

对象里有什么,我们需要在类里面去指定。

02定义类.py

# 尝试定义一个表示人的类
class Person :
    # 在类的代码块中,我们可以定义变量和函数
    # ①在类中我们所定义的变量,将会成为所有的实例的公共属性
    # 所有实例都可以访问这些变量
    name = 'swk' # 公共属性,所有实例都可以访问

    # ②在类中定义函数,类中的定义的函数,我们称为方法
    # 这些方法可以通过该类的所有实例来访问
    
    def say_hello(self) :
        # 方法每次被调用时,解析器都会**自动传递第一个实参**
        # 第一个参数,就是调用方法的对象本身,
        #   如果是p1调的,则第一个参数就是p1对象
        #   如果是p2调的,则第一个参数就是p2对象
        # 一般我们都会习惯将这个参数命名为self。self就是自己的意思。当然我们也可以叫其他的,比如a,b等都可以
        # 在别的语言中,比如java,会选择用this关键字表示
        print(self)
        # say_hello()这个方法,可以显示如下格式的数据:
        #   你好!我是 xxx
        #   注意:在方法中不能直接访问类中的属性。print('你好!我是 %s' %name)是会报错。
        #   print('你好!我是 %s' %p1.name)和print('你好!我是 %s' %p2.name)是正常执行。但是这样写,就直接写死了。
        print('你好!我是 %s' %self.name)

# 创建Person的实例
p1 = Person()
p2 = Person()

# print(p2.name)

# 调用方法,对象.方法名()   
**# 方法调用和函数调用的区别:①调用方式的区别,函数可以直接调用。方法是对象.方法名()**
# ②主要区别:
# 如果是函数调用,则调用时传几个参数,就会有几个实参
**# 但是如果是  方法调用 ,默认一定会传递一个参数,所以方法中至少要定义一个形参。**
# 有个报错信息。TypeError:say_hello()takes 0 positional arguments but 1 was given。say_hello()需要0个位置参数,但是却给了一个参数。

# 修改p1的name属性
p1.name = '猪八戒'
p2.name = '沙和尚'

p1.say_hello() # # <__main__.Person object at 0x000001B6685AAA58> 你好!我是 猪八戒
# 由于之前函数定义中有print(self)。通过结果知道打印的是一个对象。所以可以知道,self就是具体的实例对象。

p2.say_hello() # <__main__.Person object at 0x000001B6685AAA20> 你好!我是 沙和尚

# del p2.name # 删除p2的name属性

# print(p1.name) 
# print(p2.name)

创建对象的流程

p1 = Person()的运行流程
    1.创建一个变量
    2.在内存中创建一个新对象
    3.__init__(self)方法执行
    4.将对象的id赋值给变量

类中的不同对象的方法一般是一样的,但是属性是不一样的。类就是现实世界的抽象。

03 对象的初始化.py

class Person :
    # 在类中可以定义一些特殊方法(魔术方法)
    # 特殊方法都是以__开头,__结尾的方法
    # 特殊方法不需要我们自己调用,不要尝试去调用特殊方法。
    # 特殊方法将会在特殊的时刻自动调用,不需要我们手动调用。

    # 学习特殊方法的关键:
    #   1.特殊方法什么时候调用
    #   2.特殊方法有什么作用

    **# 创建对象的流程(完整):**
    # p1 = Person()的运行流程
    #   1.创建一个变量p1
    #   2.在内存中创建一个新对象,**调用类就是创建一个新对象**
    #   **3.__init__(self)方法执行**
    #   4.将对象的id赋值给变量

    # init会在对象创建以后立刻执行
    # 所以用途:**init可以用来向新创建的对象中初始化属性**
    # 调用类创建对象时,类后边的所有参数都会依次传递到init()中
    def __init__(self,name):
        # print(self)
        # 通过self向新建的对象中**初始化属性**
        self.name = name

    def say_hello(self):
        print('大家好,我是%s'%self.name)

# 目前来讲,对于Person类来说name是必须的,并且每一个对象中的name属性基本上**都是不同**
# 而我们现在是将name属性在定义为对象以后,手动添加到对象中,这种方式很容易出现错误。容易在对象中忘记添加属性,这样就会报错。
# 我们希望,**在创建对象时,必须设置name属性,如果不设置对象将无法创建**
#   并且属性的创建应该是自动完成的,而不是在创建对象以后手动完成。如果是手动完成,一旦忽略,就会报错。
# p1 = Person()
# # 手动向对象添加name属性
# p1.name = '孙悟空'

# p2 = Person()
# p2.name = '猪八戒'

# p3 = Person()
# p3.name = '沙和尚'

# p3.say_hello()

p1 = Person('孙悟空')
p2 = Person('猪八戒')
p3 = Person('沙和尚')
p4 = Person('唐僧')
# p1.__init__() 可以执行,不会报错,但是不要这么做

# print(p1.name)
# print(p2.name)
# print(p3.name)
# print(p4.name)

p4.say_hello()

类的基本结构

class 类名([父类]) :

    **公共的属性...**

    # 对象的初始化方法
    def __init__(self,...):
        ...

    # 其他的方法
    def method_1(self,...):
        ...

    def method_2(self,...):
        ...

    ...

- 练习:
    尝试自定义一个表示狗的类(Dog)
        属性:
            name
            age
            gender
            height
            ...
        方法:
            jiao()
            yao()
            run()
            ...

04 练习.py

class Dog:
    '''
        表示狗的类
    '''
    def __init__(self , name , age , gender , height):
        self.name = name
        self.age = age
        self.gender = gender
        self.height = height

    def jiao(self):
        '''
            狗叫的方法
        '''
        print('汪汪汪~~~')

    def yao(self):
        '''
            狗咬的方法
        '''  
        print('我咬你~~')

    def run(self):
        print('%s 快乐的奔跑着~~'%self.name)     

d = Dog('小黑',8,'male',30)

# 目前我们可以直接通过 对象.属性 的方式来修改属性的值,这种方式导致对象中的属性可以随意修改
#   非常的不安全,值可以任意修改,不论对错
# 现在我们就需要一种方式来**增强数据的安全性**,这个应该怎么做呢?
#   1.属性不能随意修改(我让你改你才能改,不让你改你就不能改)
#   2.属性不能修改为任意的值(年龄不能是负数)
d.name = '阿黄'
d.age = -10
d.run()

print(d.age)

# print(d.name , d.age , d.gender , d.height)

封装,①藏起来一部分②显示一部分,如果全藏起来,那也没有意义了。就像穿衣服,该遮起来的地方遮起来,不该遮起来的地方,给漏出来。对象也是一样。

05 封装.py

# 封装是面向对象的三大特性之一
# 封装指的是隐藏对象中一些不希望被外部所访问到的属性或方法

# 如何隐藏一个对象中的属性?
#   - 最简单的方式,将对象的属性名,修改为一个外部不知道的名字

# 如何获取(修改)对象中的属性?
#   - 需要提供一个getter和setter方法使外部可以访问到属性。通过这两个方法来操作属性。
#   - **getter 获取对象中的指定属性**(get_属性名)
#   - **setter 用来设置对象的指定属性**(set_属性名)

# 使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性
#   1.隐藏了属性名,使调用者无法随意的修改对象中的属性
#   2.增加了getter和setter方法,很好的控制的属性是否是只读的
#       如果希望属性是只读的,则可以直接去掉setter方法
#       如果希望属性不能被外部访问,则可以直接去掉getter方法
#   3.使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
#   4.使用getter方法获取属性,使用setter方法设置属性
#       可以在读取属性和修改属性的同时做一些其他的处理。
#       比如说,某个属性发生变化了(getter),就可以写代码给管理员发信息说产生变化了。
#       如果用户改密码了(setter),马上给用户发信息
#   5.使用getter方法可以表示一些计算的属性。比如矩形的长宽确定后,面积就可以不用setter了,因为已经固定下来。

class Dog:
    '''
        表示狗的类
    '''
    def __init__(self , name , age):
        self.hidden_name = name
        self.hidden_age = age

    def say_hello(self):
        print('大家好,我是 %s'%self.hidden_name) 

    def get_name(self):
        '''
            get_name()用来获取对象的name属性
        '''    
        print('用户读取了属性')
        return self.hidden_name  # 有返回值

    def set_name(self , name):
        print('用户修改了属性')
        self.hidden_name = name  # 没返回值

    def get_age(self):
        return self.hidden_age

    def set_age(self , age):
        if age > 0 :  # 这里可以对数据进行检查和判断
            self.hidden_age = age    

d = Dog('旺财',8)
print(d.get_name()) # 旺财

# d.say_hello()

# 调用setter来修改name属性 
d.set_name('小黑')
d.set_age(-10)

# d.say_hello()
print(d.get_age()) # 8

06 封装.py

class Rectangle:
    '''
        表示矩形的类
    '''
    def __init__(self,width,height):
        self.hidden_width = width
        self.hidden_height = height

    def get_width(self):
        return self.hidden_width

    def get_height(self):
        return self.hidden_height   

    def set_width(self , width):
        self.hidden_width = width 

    def set_height(self , height):
        self.hidden_height = height 

    def get_area(self):  #面积是不需要设置的,因为长宽确定后,面积是定了
        return self.hidden_width * self.hidden_height        

# r = Rectangle(5,2)  
# r.set_width(10)
# r.set_height(20)

# print(r.get_area())     
 
 
# 可以为对象的属性使用双下划线开头,__xxx。本质上也是给属性改了个名字。
# 双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
# 其实隐藏属性只不过是Python自动为属性改了一个名字
#   实际上是将名字修改为了,_类名__属性名 比如 __name ->改成了 _Person__name。这个意义在于,防君子不防小人。
# 上面这种方式其实不推荐使用。
class Person:
    def __init__(self,name):
        self.__name = name

    def get_name(self):
        return self.__name

    def set_name(self , name):
        self.__name = name

p = Person('孙悟空')

# print(p.__name) # __开头的属性是隐藏属性,无法通过对象访问。报错:AttributeError: 'Person' object has no attribute '__name'
p.__name = '猪八戒'
print(p._Person__name) # 孙悟空  p.__name = '猪八戒' 代码 没有发挥作用
p._Person__name = '猪八戒'

print(p.get_name()) # 猪八戒

#常用情况;
#有些情况下,我们希望可以隐藏的更彻底,不让外部访问
# 使用__开头的属性,实际上依然可以在外部访问,所以这种方式我们一般不用
#   一般我们会将一些私有属性(不希望被外部访问的属性)以_开头
#   一般情况下,使用_开头的属性(方法也是一样的)都是私有属性,
#   这种形式表示:建议没有特殊需要不要修改私有属性,实际还是可以修改。
class Person:
    def __init__(self,name):
        self._name = name

    def get_name(self):
        return self._name

    def set_name(self , name):
        self._name = name   

p = Person('孙悟空')

print(p._name) #孙悟空

07 封装.py

class Person:
    def __init__(self,name,age):
        self._name = name
        self._age = age

    # **property装饰器,用来将一个get方法,转换为对象的属性**
    # 添加为property装饰器以后,我们就可以**像调用属性一样使用get方法**
    # 使用property装饰的方法,必须和属性名是一样的
    @property    
    def name(self):
        print('get方法执行了~~~')
        return self._name

    # setter方法的装饰器:@属性名.setter
    @name.setter    
    def name(self , name):
        print('setter方法调用了')
        self._name = name        

    @property
    def age(self):
        return self._age

    @age.setter    
    def age(self , age):
        self._age = age   

        

p = Person('猪八戒',18)

p.name = '孙悟空'  # 如果没有设置set方法,是会报错的。
p.age = 28

print(p.name,p.age)  # 实际上就是调用get()方法。比使用get_name还有p.name()要省事一些

# setter方法调用了
# get方法执行了~~~
# 孙悟空 28

这两个装饰器的作用,就是让我们用属性的方式去操作方法。这样更方便一些。实际调用的是方法。
这两个装饰器有递进关系,必须现有getter才能有setter。不然会报错。

继承,就像子女从父母那里继承财产那样。

08 继承.py

# 继承

# 定义一个类 Animal(动物)
#   这个类中需要两个方法:run() sleep() 
class Animal:
    def run(self):
        print('动物会跑~~~')

    def sleep(self):
        print('动物睡觉~~~')

    # def bark(self):
    #     print('动物嚎叫~~~')   

# 定义一个类 Dog(狗)
#   这个类中需要三个方法:run() sleep() bark()
# class Dog:
#     def run(self):
#         print('狗会跑~~~')

#     def sleep(self):
#         print('狗睡觉~~~')

#     def bark(self):
#         print('汪汪汪~~~') 

# 有一个类,能够实现我们需要的**大部分功能**,但是不能实现全部功能
# 如何能让这个类来实现全部的功能呢?
#   ① 直接修改这个类,在这个类中添加我们需要的功能
#       - 修改起来会比较麻烦,并且会违反OCP原则
#   ② 直接创建一个新的类
#       - 创建一个新的类比较麻烦,并且需要大量的进行复制粘贴,会出现大量的重复性代码
#   ③ 直接从Animal类中来继承它的属性和方法
#       - 继承是面向对象三大特性之一
#       - 通过继承我们可以使一个类获取到其他类中的**属性和方法  都会被继承** 
#       - 在定义类时,可以在类名后的括号中指定当前类的父类(超类、基类、super)
#           子类(衍生类)可以直接继承父类中的所有的属性和方法
#           
#  通过继承可以直接让子类获取到**父类的方法或属性**,避免编写重复性的代码,并且也符合OCP原则
#   所以我们经常需要通过继承来**对一个类进行扩展**

class Dog(Animal):
    def bark(self):
        print('汪汪汪~~~') 

    def run(self):
        print('狗跑~~~~')    

class Hashiqi(Dog):
    def fan_sha(self):
        print('我是一只傻傻的哈士奇')        

d = Dog()
h = Hashiqi()

# d.run()
# d.sleep()
# d.bark()

# r = isinstance(d , Dog)
# r = isinstance(d , Animal)
# print(r)

# 在创建类时,如果省略了父类,则默认父类为object
#   object是所有类的父类,所有类都继承自object
class Person(object):
    pass

# issubclass() 检查一个类是否是另一个类的子类
# print(issubclass(Animal , Dog))
# print(issubclass(Animal , object)) #检查Animal是不是object 的子类
# print(issubclass(Person , object))

# isinstance()用来检查一个对象是否是一个类的实例
#   如果这个类是这个对象的父类,也会返回True
#   所有的对象都是object的实例
print(isinstance(print , object))  # True

09 重写.py

# 继承

# 定义一个类 Animal(动物)
#   这个类中需要两个方法:run() sleep() 
class Animal:
    def run(self):
        print('动物会跑~~~')

    def sleep(self):
        print('动物睡觉~~~')

class Dog(Animal):
    def bark(self):
        print('汪汪汪~~~') 

    def run(self):
        print('狗跑~~~~')    

# 如果在子类中如果有和父类同名的方法,则通过子类实例去调用方法时,
#   会调用子类的方法而不是父类的方法,这个特点我们成为叫做方法的重写(覆盖,override)
# 创建Dog类的实例
# d = Dog()

# d.run()

# 当我们调用一个对象的方法时,
#   会优先去**当前对象**中寻找是否具有该方法,如果有则直接调用
#   如果没有,则去当前对象的父类中寻找,如果父类中有则直接调用父类中的方法,
#   如果没有,则去父类的父类中寻找,以此类推,直到找到object,如果依然没有找到,则报错
class A(object):
    def test(self):
        print('AAA')

class B(A):
    def test(self):
        print('BBB')

class C(B):
    def test(self):
        print('CCC')   

# 创建一个c的实例
c = C()
c.test() # CCC

10 继承.py

class Animal:
    def __init__(self,name):
        self._name = name  **# 设置属性**

    def run(self):
        print('动物会跑~~~')

    def sleep(self):
        print('动物睡觉~~~')

    @property
    def name(self):
        return self._name

    @name.setter    
    def name(self,name):
        self._name = name

# **父类中的 所有方法 都会被子类继承,包括 特殊方法 ,也可以重写特殊方法,毕竟子类是会有比父类多的属性的情况**
class Dog(Animal):

    def __init__(self,name,age): # 有自己的init()就不会调用父类的init()。这是因为 把父类的__init__方法给重写、覆盖了。

        # self.name= name 假如要继承父类的属性很多的话,使用这样的写法就会重复写代码很多次,非常麻烦。所以要想办法省略。便有了如下的代码形式。

        # 希望可以直接调用父类的__init__**来初始化父类中定义的属性**
        # Animal.__init__(self,name)必须要传递self,不传会报错。因为是直接通过类来调用.__init__方法,而不是对象调用。
        # 上面这样写有问题,Animal不是动态的,是写死的。假如有一天,父类改变了,这样写就不合适了。
                # 用super()替代,灵活性更强了。即使父类发生了变化,代码也不会有影响。

        **# super() 可以用来获取当前类的父类**,
        #   并且通过super()返回**对象调用父类方法**时,不需要传递self
        super().__init__(name)

        self._age = age

    def bark(self):
        print('汪汪汪~~~')

    def run(self):
        print('狗跑~~~~')

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self,age):
        self._age = name

d = Dog('旺财',18) 

print(d.name)      # 旺财
print(d.age)       # 18

11 多重继承.py

class A(object):
    def test(self):
        print('AAA')

class B(object):
    def test(self):
        print('B中的test()方法~~')

    def test2(self):
        print('BBB') 

# 在Python中是支持多重继承的,也就是我们可以为一个类同时指定多个父类,就类比人认了多个干爹吧。在其他语言中,有的是不能指定多个父类的。
#   可以在类名的()后边添加多个类,来实现多重继承
#   多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法

# 在开发中没有特殊的情况,应该尽量避免使用多重继承,因为多重继承会让我们的代码过于复杂
# 如果多个父类中有同名的方法,则会现在第一个父类中寻找,然后找第二个,然后找第三个。。。
#   前边父类的方法会覆盖后边父类的方法
class C(A,B):
    pass

# 类名.__bases__ 这个属性可以用来获取当前类的所有父类    
print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B'>) 外面套了个括号,所以这是个元组,元组里面是可以放多个元素
print(B.__bases__) # (<class 'object'>,)

c = C()
c.test() # AAA
c.test2() # BBB

12 多态.py

# 多态是面向对象的三大特征之一
# 多态从字面上理解是多种形态
# 狗(狼狗、藏獒、哈士奇、古牧 。。。)
# 一个对象可以以不同的形态去呈现

# 定义两个类
class A:
    def __init__(self,name):
        self._name = name

    @property
    def name(self):
        return self._name
        
    @name.setter
    def name(self,name):
        self._name = name   

class B:
    def __init__(self,name):
        self._name = name

    def __len__(self):
        return 10

    @property
    def name(self):
        return self._name
        
    @name.setter
    def name(self,name):
        self._name = name   

class C:
    pass

a = A('孙悟空')
b = B('猪八戒')
c = C()

# 定义一个函数
# 对于say_hello()**这个函数**来说,只要对象中含有name属性,**这个对象**就可以作为参数传递
#   这个函数并不会考虑对象的类型(无所谓ABC类),只要有name属性就能作为参数传递进来。这就是多态
def say_hello(obj):
    print('你好 %s'%obj.name)

# 在say_hello_2中我们做了一个类型检查,也就是只有obj是A类型的对象时,才可以正常使用,
#   其他类型的对象都无法使用该函数,这个函数就违反了多态,因为这个时候,obj必须是A类型才能执行代码,也就是只有一种形态
# 违反了多态的函数,只适用于一种类型的对象,无法处理其他类型对象,这样导致函数的通用性非常的差
# 注意,向isinstance()这种函数,在开发中一般是不会使用的!因为一旦去检测对象类型,就违反了多态的原则!
def say_hello_2(obj):
    # 做类型检查
    if isinstance(obj , A):  #obj是否是A的实例
        print('你好 %s'%obj.name)    
# say_hello(b)    
# say_hello_2(b)

# 多态类型举例:
# 鸭子类型
# 如果一个东西,走路像鸭子,叫声像鸭子,那么它就是鸭子
# 所以程序员只关注功能,不关注究竟具体是什么类型。

# len()
# 之所以一个对象能通过len()来获取长度,是因为对象中具有一个特殊方法__len__
# 换句话说,只要对象中具有__len__特殊方法,就可以通过len()来获取它的长度
l = [1,2,3]
s = 'hello'

print(len(l)) # 3
print(len(s)) # 5
#通过上面代码可知,不管是列表类型还是字符串类型,都能通过len()实现我们的功能。

print(len(b)) # 10
# print(len(c))  # TypeError: object of type 'C' has no len() 执行报错。因为类型C里面没有魔法函数__len__()

**# 面向对象的三大特征(所有面向对象的语言都必备,下面简单理解):**
#   封装 把属性藏到代码内部,想让你看的就展示出来,不想让看的就隐藏起来
#       - 确保对象中的数据安全
#   继承  对一个类进行扩展
#       - 保证了对象的可扩展性
#   多态  让方法的定义更灵活,不用去考虑对象的具体类型,只要你满足我们的具体要求,就可以做完函数的参数进行传递
#       - 保证了程序的灵活性。如果我们的方法都和对象的类型绑定的话,会导致代码的通用性很差,缺乏灵活性。

13 类中的属性和方法.py

# 定义一个类
class A(object):

    **# 类属性
    # 实例属性
    # 类方法
    # 实例方法
    # 静态方法 (当函数  不涉及对象或对象数据的使用时,考虑使用静态方法 )**

    # 类属性,直接在类中定义的属性是类属性
    #   类属性可以通过类或类的实例访问到
    #   但是类属性只能通过类对象来修改,无法通过实例对象修改。因为实例对象只会添加这个属性,或者修改覆盖这个属性,不会影响类属性。
    count = 0

    def __init__(self):
        # 实例属性,通过实例对象添加的属性属于实例属性。self表示当前对象
        #   实例属性只能通过实例对象来访问和修改,类对象无法访问修改
        self.name = '孙悟空'

    # 实例方法
    #   在类中定义,以self为第一个参数的方法都是实例方法
    #   实例方法在调用时,Python会将调用对象作为第一个参数self传入
    #   实例方法可以通过实例和类去**调用**
    #       当通过实例**调用**时,会自动将当前调用对象作为self传入
    #       当通过类**调用**时,不会自动传递self,此时我们必须手动传递self。下面的代码:a.test() #等价于 A.test(a)
    def test(self):
        print('这是test方法~~~ ' , self)    

    # 类方法    
    # 在类内部使用 @classmethod 来修饰的方法属于类方法
    # 类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象,是class的简写,不能和关键字class一样。对比下实例方法第一个参数是self。
    #   类方法和实例方法的区别,实例方法的第一个参数是self,而类方法的第一个参数是cls。
    #   类方法可以通过类去调用,也可以通过实例调用,没有区别

    # 这个cls是谁,这个方法在哪个类里定义的,cls就是谁。在A类里定义的,那cls就是A这个类。
    @classmethod
    def test_2(cls):
        print('这是test_2方法,他是一个类方法~~~ ',cls)
        print(cls.count)

    # 静态方法
    # 在类中使用 @staticmethod 来修饰的方法属于静态方法  
    # 静态方法不需要指定任何的默认参数,静态方法可以通过**类和实例**去**调用**。对比实例方法和静态方法都需要传入参数,这个不需要。
    # 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数,需要类来管理。
    # 静态方法一般**都是一些工具方法**,和当前类无关
    # 举例理解:静态方法,就像汽车里的储物箱,用来存东西。但是车子和这个箱子是没有关系的。这个箱子我放到任何一个车子里都行,这个车子没有这个箱子也是可以正常运行。

    @staticmethod
    def test_3():
        print('test_3执行了~~~')

a = A()
# 实例属性,通过实例对象添加的属性属于实例属性
# a.count = 10  # 这个操作,实际是给对象里添加一个属性。不影响类属性
# A.count = 100
# print('A ,',A.count) 
# print('a ,',a.count) 
# print('A ,',A.name) 
# print('a ,',a.name)   

a.test() #等价于 A.test(a)    输出:这是test方法~~~  <__main__.A object at 0x0000020177142940>

A.test_2() #等价于 a.test_2()   输出:这是test_2方法,他是一个类方法~~~ 0 <class '__main__.A'>

A.test_3() # test_3执行了~~~
a.test_3() # test_3执行了~~~
# 静态方法既可以通过类去调用,也可以通过实例方法去调用。

14 垃圾回收.py

# 就像我们生活中会产生垃圾一样,程序在运行过程当中也会产生垃圾
# 程序运行过程中产生的垃圾会影响到程序的运行的运行性能,所以这些垃圾必须被及时清理
# 没用的东西就是垃圾
# 在程序中**没有被引用的对象就是垃圾**,这种垃圾对象过多以后会影响到程序的运行的性能
#   所以我们必须进行及时的垃圾回收,所谓的垃圾回收就是将垃圾对象从内存中删除
# 在Python中有自动的垃圾回收机制,它会自动将这些没有被引用的对象删除,
#   所以我们不用手动处理垃圾回收
# 程序结束以后,代码里的对象就会自动删除

class A:
    def __init__(self):
        self.name = 'A类'

    # del是一个特殊方法,它会在对象被垃圾回收前调用
    def __del__(self):
        print('A()对象被删除了~~~',self)

a = A()
b = a # 又使用一个变量b,来引用a对应的对象

print(a.name)

# a = None # 将a设置为了None,此时没有任何的变量对A()对象进行引用,它就是变成了垃圾
# b = None
# del a  # 这个只是把变量给删除了,并没有把对象给删除
# del b  # 当把这两个变量给删除了,对象就没有变量引用了
input('回车键退出...')

15 特殊方法.py    也叫魔法方法

# 特殊方法,也称为魔术方法
# 特殊方法都是使用__开头和结尾的
# 特殊方法一般不需要我们手动调用,需要在一些特殊情况下自动执行

# 研究特殊方法就关心两个问题:①特殊方法什么时候调用②它有什么用
# **特殊方法主要是用来配合多态**。举例:加法,既可以对数字也可以对字符串进行加法。比较,字符串和数字都是可以进行比较的。等。

# 定义一个Person类
class Person(object):
    """人类"""
    def __init__(self, name , age):
        self.name = name
        self.age = age

    __str__()这个特殊方法会在尝试**将对象转换为字符串的时候调用**
    # 它的作用可以用来**指定对象转换为字符串的结果**  (print函数)  
    def __str__(self):
        return 'Person [name=%s , age=%d]'%(self.name,self.age)        

    # __repr__()这个特殊方法会在对当前对象使用repr()函数时调用
    # 它的作用是指定对象直接在 ‘交互模式’中直接输出的效果    str和repr适合不同的场合
    def __repr__(self):
        return 'Hello'        

    # object.__add__(self, other)
    # object.__sub__(self, other)
    # object.__mul__(self, other)
    # object.__matmul__(self, other)
    # object.__truediv__(self, other)
    # object.__floordiv__(self, other)
    # object.__mod__(self, other)
    # object.__divmod__(self, other)
    # object.__pow__(self, other[, modulo])
    # object.__lshift__(self, other)
    # object.__rshift__(self, other)
    # object.__and__(self, other)
    # object.__xor__(self, other)
    # object.__or__(self, other)

    # object.__lt__(self, other) 小于 <
    # object.__le__(self, other) 小于等于 <=
    # object.__eq__(self, other) 等于 ==
    # object.__ne__(self, other) not equal不等于 !=
    # object.__gt__(self, other) 大于 >
    # object.__ge__(self, other) 大于等于 >= 
    
    # __len__()获取对象的长度

    # object.__bool__(self)
    # 可以通过bool来指定对象转换为布尔值的情况
    def __bool__(self):
        return self.age > 17

    # __gt__会在对象做大于比较的时候调用,该方法的返回值将会作为比较的结果
    # 他需要两个参数,一个self表示当前对象,other表示和当前对象比较的对象
    # self > other
    def __gt__(self , other):
        return self.age > other.age

# 创建两个Person类的实例        
p1 = Person('孙悟空',18)
p2 = Person('猪八戒',28)

# 打印p1
# 当我们打印一个对象时,实际上打印的是对象的中特殊方法 __str__()的返回值
print(p1) # Person [name=孙悟空 , age=18]
# print(p1)
# print(p2)

# print(repr(p1))

t = 1,2,3
print(t) # (1, 2, 3)  t当然是一个对象。

# print(p1 > p2)
# print(p2 > p1)

# print(bool(p1))

# if p1 :
#     print(p1.name,'已经成年了')
# else :
#     print(p1.name,'还未成年了')

拓展dict.py

class A(object):
    a = 0  # 类属性

    def __init__(self):
        self.b =1   # 实例属性

aa = A()
# 返回类内部所有属性和方法对应的字典
print(A.__dict__)  # {'__module__': '__main__', 'a': 0, '__init__': <function A.__init__ at 0x000002068DB75840>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

# 返回实例属性和值组成的字典。打印的是对象的实例属性。
print(aa.__dict__) # {'b': 1}

无论是类还是对象,调用.init返回结果的都是一个字典。

16 模块.py

# 模块(module)
# 模块化,模块化指将一个完整的程序分解为一个一个小的模块
#   通过将模块组合,来搭建出一个完整的程序
# 不采用模块化,统一将所有的代码编写到一个文件中
# 采用模块化,将程序分别编写到多个文件中
#   模块化的有点:
#       ① 方便开发
#       ② 方便维护。想更新、想换直接换。
#       ③ 模块可以复用!

# 在Python中一个py文件就是一个模块,要想创建模块,实际上就是创建一个python文件
# 注意:模块名要符合标识符的规范

# 在一个模块中引入外部模块,使用别的模块里面的代码。
# ① import 模块名 (模块名,就是python文件的名字,注意不要py)
# ② import 模块名 as 模块别名
#   - 可以引入同一个模块多次,但是**模块的实例对象**只会创建一个
#   - import可以在程序的任意位置调用,但是一般情况下,import语句都会统一写在程序的开头
#   - 在每一个模块内部都有一个__name__属性,通过这个属性可以获取到模块的名字
#   - **__name__属性值为 __main__的模块是主模块,一个程序中只会有一个主模块
#       主模块就是我们直接通过 python 执行的模块**
import test_module as test  # 会有输出:我是test_module模块

print(test)  # <module 'test_module' from 'C:\\Users\\xxx\\Desktop\\Python核心基础\\01-代码-笔记\\lesson_06_对象\\code\\test_module.py'> 后面是文件的路径
print(test.__name__) # test_module     test_module.py是我们引入的模块,所以它的名字就是模块名。模块名在这个文件中,就相当于是一个变量。既然是变量,那就要符合标识符的规范
print(__name__) # __main__

其实,模块就相当于是一个大的容器。容器里可以存一些数据,存一些代码,存一些东西。

需要的时候,直接引入模块,直接使用就完了。(下面两个代码文件要连起来看)

m.py

#下面就是模块的标准书写方式。

# 可以在模块中**定义变量**,在模块中定义的变量,在引入模块后,就可以直接使用了
a = 10
b = 20

# 添加了_的变量,只能在模块内部访问。只有在通过import * 引入时,是不会引入_开头的变量。
_c = 30

# 可以在模块中定义函数,同样可以通过模块访问到
def test():
    print('test')

def test2():
    print('test2')

# 也可以定义类    
class Person:
    def __init__(self):
        self.name = '孙悟空'

# 编写测试代码,这部分代码,只要当当前文件作为主模块的时候才需要执行
#   而当模块被其他模块引入时,不需要执行的,此时我们就必须要检查当前模块是否是主模块。
#   也就是测试代码,不需要作为模块的代码(会被执行的代码)都放到判断里,这样作为模块被执行的时候,这些代码不会被执行。
if __name__ == '__main__':
    test() # test
    test2() # test2
    p = Person()
    print(p.name) # 孙悟空

17 模块.py

一、
# import m

# # 访问模块中的变量:模块名.变量名
# print(m.a , m.b) # 10  20
#
# m.test2() # test2
#
# p = m.Person()
#
# print(p) # <m.Person object at 0x0000026FB508A080>  # m就是模块名
# print(p.name) # 孙悟空

def test2():
    print('这是主模块中的test2')

二、
# 也可以只引入模块中的部分内容
# 语法 from 模块名 import 变量,变量....
# from m import Person  # 引入类
# # from m import test  # 引入函数
# # from m import Person,test
# # from m import * # 引入到模块中所有变量/内容,一般不会使用。
# # 因为这样就相当于把模块里的所有代码复制到当前的Python文件中。当两个文件中有同名的变量是,会出现后面的代码覆盖前面文件的代码的情形
#
# p1 = Person()
# print(p1)  # <m.Person object at 0x000001244D94A748>  这样就**避免**了在前面**还要输入  模块名.**  的形式
# # test()
# # test2()

三、
# # 也可以为引入的变量使用别名
# # 语法:from 模块名 import 变量 as 别名
# from m import test2 as new_test2  #假如引入的变量名和当前文件的变量名出现重名的情况,这样使用就避免覆盖当前文件变量名相关代码的情况
#
# test2() # 这是主模块中的test2         这个就是当前文件中的**函数调用**。
# new_test2() # test2       这个是引入模块中的函数调用。

# import m 这个时候就是把整个模块给引入了,里面所有的东西都可以看见
# print(m._c) # 30  这样是可以输出_c变量的。

from m import *
print(_c) # NameError: name '_c' is not defined 报错信息。

# import xxx
# import xxx as yyy
# from xxx import yyy , zzz , fff
# from xxx import *
# from xxx import yyy as zz

定义一个模块,我们不希望模块里的所有东西都能被外部访问。我希望模块中的有一些东西是私有的,我希望有些东西外部是看不见的,只能内部访问。

我们想在模块里创建私有属性应该怎么办?上面m.py文件中的代码表现了这一点:_c = 30

包的好处是什么,可以把一堆相关的模块同一放到一起。

看到cache,就要知道这样的都是一个临时的文件,是一个缓存的文件。

a.py

c = 30

b.py

d = 40

18 包.py

# 包 Package
# **包也是一个模块,只不过是一个比较强大的模块**
# 当我们模块中代码过多时,或者一个模块需要被分解为多个模块时,总之,当模块过大的时候,就需要使用到包
# 普通的模块就是一个py文件,而包是一个文件夹
#   包中必须要一个一个 __init__.py 这个文件,这个文件中可以包含有包中的主要内容

**from hello import a , b  # 引入hello文件夹里面的a b模块**

print(a.c) # 30
print(b.d) # 40

# __pycache__ 是模块的缓存文件
# py代码在交给计算机执行前,需要被解析器先转换为机器码(转化成1010这种二进制码结构,但是显示是以16进制显示的),然后交给计算机再执行
#   所以我们在使用模块(包)时,也需要将模块的代码先转换为机器码然后再交由计算机执行
#   而为了提高程序运行的性能,python会在编译过一次以后,将代码保存到一个缓存文件中,避免重复编译
#   这样在下次加载这个模块(包)时,就可以不再重新编译而是直接加载缓存中编译好的代码即可。总之,就是为了提高我们效率

19 Python的标准库

# 开箱即用
# 为了实现开箱即用的思想,Python中为我们提供了**一个有很多模块的标准库**
# 在这个标准库中,有很多很强大的**模块**我们可以直接使用,
#   并且标准库会随Python的安装一同安装
# sys模块,它里面提供了一些变量和函数,使我们可以获取到Python解析器的信息
#   或者通过函数来操作Python解析器

# 引入sys模块
import sys
print(sys) # <module 'sys' (built-in)>  表示这是一个内置模块,表示我们可以直接引入,直接使用。

# pprint 模块它给我们提供了一个方法 pprint() 该方法可以用来对打印的数据做简单的格式化
import pprint

# sys.argv
# 获取执行代码时,命令行中所包含的参数
# 返回的是一个中括号,表示该属性是一个列表,列表中保存了当前命令的所有参数。因为命令参数可以有多个
# print(sys.argv)  # ['C:/Users/xxx/Desktop/Python核心基础/01-代码-笔记/lesson_06_对象/code/19.Python的标准库.py']

# sys.modules
# 获取当前程序中引入的所有模块。程序要执行,首先要加载模块,想知道加载了哪些模块
# modules是一个字典,字典的key是模块的名字,字典的value是模块对象
# pprint.pprint(sys.modules)  # 第一个pprint是模块;第二个pprint是模块里的函数

# sys.path
# 他是一个列表,列表中保存的是模块的搜索路径。
# pprint.pprint(sys.path)

# ['C:\\Users\\xxx\\Desktop\\Python核心基础\\01-代码-笔记\\lesson_06_对象\\code',   先去当前路径下找有没有这个模块,没有再往下个路径找
#  'D:\\PythonCode\\demo_test_study',
#  'D:\\Develop_software\\Pycharm\\PyCharm '
#  '2019.3.1\\plugins\\python\\helpers\\pycharm_display',
#  'D:\\Develop_software\\Anaconda3\\python36.zip',
#  'D:\\Develop_software\\Anaconda3\\DLLs',
#  'D:\\Develop_software\\Anaconda3\\lib',
#  'D:\\Develop_software\\Anaconda3',
#  'D:\\Develop_software\\Anaconda3\\lib\\site-packages',
#  'D:\\Develop_software\\Anaconda3\\lib\\site-packages\\win32',
#  'D:\\Develop_software\\Anaconda3\\lib\\site-packages\\win32\\lib',
#  'D:\\Develop_software\\Anaconda3\\lib\\site-packages\\Pythonwin',
#  'D:\\Develop_software\\Pycharm\\PyCharm '
#  '2019.3.1\\plugins\\python\\helpers\\pycharm_matplotlib_backend']

# sys.platform
# 表示当前Python运行的平台
# print(sys.platform)

# sys.exit()
# 函数用来退出程序
# sys.exit('程序出现异常,结束!')
# print('hello')

# os 模块让我们可以对操作系统进行访问
import os
print(os) #<module 'os' from 'D:\\Develop_software\\Anaconda3\\lib\\os.py'>  显示源码在哪里

# os.environ
# 通过这个属性可以获取到系统的环境变量
pprint.pprint(os.environ['path'])

# os.system()
# 可以用来执行操作系统的名字
# os.system('dir')
os.system('notepad')

第七章 异常和文件

异常

程序在运行过程当中,不可避免的会出现一些错误,比如:
    使用了没有赋值过的变量
    使用了不存在的索引
    除0
    ...
这些错误在程序中,我们称其为异常。
程序运行过程中,一旦出现异常将会导致程序立即终止,异常以后的代码全部都不会执行!

处理异常

程序运行时出现异常,**目的**并不是让我们的程序直接终止!
Python是希望在出现异常时,我们可以编写代码来对异常进行处理!

如果我们把一个程序比喻成一条路的话,执行代码的过程就是沿着这条路笔直的走。但是有可能在这条路上会有坑,
如果笔直走的话是可能掉进这个坑里,异常,就相当于这个坑,掉进坑里这个代码就不能继续执行了。
我们的目的就是要处理这个异常,解决掉前进道路上有坑的问题。

try语句
    try:
        代码块(可能出现错误的语句)
    except 异常类型 as 异常名:
        代码块(出现错误以后的处理方式)
    except 异常类型 as 异常名:
        代码块(出现错误以后的处理方式)
    except 异常类型 as 异常名:
        代码块(出现错误以后的处理方式)
    else:
        **代码块(没出错时要执行的语句)**
    finally:
        代码块(该代码块总会执行)

    try是必须的 else语句有没有都行
    except和finally至少有一个

可以将可能出错的代码放入到try语句,这样如果代码没有错误,则会正常执行,
    如果出现错误,则会执行expect子句中的代码,这样我们就可以通过代码来处理异常
    **避免因为一个异常导致整个程序的终止**

异常的传播(所以也叫 抛出异常)

异常的出现无非就两个位置,①出现在全局里 ②出现在函数里

当**在函数中**出现异常时,如果在函数中对异常进行了处理,则异常不会再继续传播,
    如果函数中没有对异常进行处理,则异常会继续**向函数调用**处传播,
    如果函数调用处处理了异常,则不再传播,如果没有处理则继续向**调用处**传播
    直到传递到全局作用域(主模块)如果**依然没有处理**,则程序终止,并且显示异常信息

当程序运行过程中出现异常以后,所有的异常信息会被保存一个专门的**异常对象**中,
    而异常传播时,实际上就是异常对象抛给了调用处   所以 **抛** 的就是**异常对象**
    比如 :ZeroDivisionError**类**的对象专门用来表示除0的异常。当出现一个除以0的错误以后,**会创建**一个ZeroDivisionError**类的实例**
            NameError**类**的对象专门用来处理变量错误的异常。当出现命名错误以后,**会创建**一个NameError**类的实例**
            ....

在Python为我们提供了多个异常对象

01 异常.py

# # print('hello')
# try:
#     # try中放置的是有可能出现错误的代码
#     print(10/2)
# except:
#     # except中放置的是出错以后的处理防暑
#     print('哈哈哈,出错了~~~')
# else:
#     print('程序正常执行没有错误')
# print('你好')
#
#
# # print(10/0)

def fn():
    print('Hello fn')
    print(a)
    print(10/0)

def fn2():
    print('Hello fn2')
    fn()

def fn3():
    print('Hello fn3')
    fn2()

fn3()

# Hello fn3
# Hello fn2
# Hello fn
# Traceback (most recent call last):
#   File "C:/Users/xxx/Desktop/Python核心基础/01-代码-笔记/lesson_07_异常和文件/code/01.异常.py", line 29, in <module>
#     fn3()
#   File "C:/Users/xxx/Desktop/Python核心基础/01-代码-笔记/lesson_07_异常和文件/code/01.异常.py", line 27, in fn3
#     fn2()
#   File "C:/Users/xxxx/Desktop/Python核心基础/01-代码-笔记/lesson_07_异常和文件/code/01.异常.py", line 23, in fn2
#     fn()
#   File "C:/Users/xxxx/Desktop/Python核心基础/01-代码-笔记/lesson_07_异常和文件/code/01.异常.py", line 18, in fn
#     print(a)
# NameError: name 'a' is not defined

02 异常对象.py

print('异常出现前')
l = []
try:
    # print(c)
    # l[10]
    1 + 'hello'
    # print(10/0)
except NameError:
    # 如果except后不跟任何的内容,则此时它会捕获到**所有的**异常
    # 如果在except后跟着一个异常的类型,那么此时它**只会捕获指定的该类型**的异常
    print('出现 NameError 异常')
except ZeroDivisionError:
    print('出现 ZeroDivisionError 异常')
except IndexError:
    print('出现 IndexError 异常')
# Exception 是所有异常类的父类,所以如果except后跟的是Exception,他也会捕获到所有的异常
# 可以在异常类后边跟着一个 as xx 此时xx就是异常对象
except Exception as e :  # 表示把异常对象赋值给e这个变量,此时e就表示这个异常对象。一般情况下,并没有太大的作用。
    print('未知异常',e,type(e))  #输出:未知异常 unsupported operand type(s) for +: 'int' and 'str' <class 'TypeError'>
finally :
    print('无论是否出现异常,该子句都会执行')

print('异常出现后')

抛出异常

- 可以使用 raise 语句来抛出异常,
    raise语句后需要跟一个异常类 或 异常的实例

03 抛出异常.py

# 也可以自定义异常类,只需要创建一个类继承Exception即可。一般情况下也不是特别需要
class MyError(Exception):
    pass

def add(a,b):
    # 如果a和b中有负数,就向调用处抛出异常
    if a < 0 or b < 0:
        # raise用于向外部抛出异常,后边可以跟一个异常类,或异常类的实例
        # raise Exception     # 这里是  **跟的是一个异常类**
        # 为什么要抛出异常?抛出异常的目的,告诉调用者这里调用时出现问题,希望你自己处理一下
        # raise Exception('两个参数中不能有负数!')   # 这里是 **后面加异常类的实例,这样会显示异常信息**
        raise MyError('自定义的异常')
        
        # 也可以通过if else来代替异常的处理
        # return None    # 这种方式有个弊端。如果问题严重到必须要有人去处理,就必须要抛出异常这种方式。
    r = a + b
    return r

print(add(-123,456))

文件(File)

在计算机里,一切皆文件。

- 通过Python程序来对计算机中的各种文件进行增删改查的操作
- I/O(Input / Output)      **注意,input和output都是站着人(内存)的角度:从人(内存)这里input读取,从人(内存)这里output输出           在有的语言中,文件的操作,被称为io。**
- 操作文件的步骤:
    ① 打开文件 
    ② 对文件进行各种操作(读、写),然后保存
    ③ 关闭文件

04 打开文件.py

open(file, mode='r', buffering=-1, encoding_=None, errors=None, newline=None, closefd=True, opener=None)

**使用open函数来打开一个文件,返回的是一个对象**
# 参数:
   file **要打开的文件的名字(路径)**
# 返回值:
   **返回一个对象,这个对象就代表了当前打开的文件**

# 创建一个变量,来保存文件的名字
# 如果目标文件和当前文件在同一级目录下,则直接使用文件名即可
# file_name = 'demo.txt'

# 在windows系统使用路径时,**可以使用/(斜杠)来代替 \(反斜杠)**。在linux系统中无所谓,因为linux系统中就是使用**普通斜杠/**
# 或者可以使用 \\ 来代替 \
# 或者也可以使用原始字符串。意思是,在字符串前面加个r。此时,里面的转义字符都会被忽略。
# file_name = 'hello\\demo.txt'
# file_name = r'hello\demo.txt'

# 表示路径,**可以使用..来返回上一级目录。使用.返回的是当前这一级目录。**
file_name = './hello/demo.txt'

# 如果目标文件距离当前文件比较远,此时可以使用绝对路径
# 绝对路径应该从磁盘的根目录开始书写
# file_name = r'C:\Users\lilichao\Desktop\hello.txt'

file_obj = open(file_name) # 打开 file_name 对应的文件,返回的是一个表示文件比如pi_digits.txt的对象。

print(file_obj)  #<_io.TextIOWrapper name='./hello/demo.txt' mode='r' encoding='cp936'>

05 关闭文件.py  更好的方式是使用 with ... as,语句 专门为打开文件来设置的

# 打开文件
file_name = r'./hello/demo.txt'

# **调用open()来打开文件**
file_obj = open(file_name)

# 当我们获取了**文件对象**以后,所有的对文件的操作都应该**通过对象**来进行
# 读取文件中的内容
# read()方法,用来读取文件中的内容,它会将**全部内容**作为一个长长的字符串**存储在变量content中**

content = file_obj.read()
print(content)   
成功输出文件中的内容。

**# 关闭文件**
# 调用close()方法来手动关闭文件。
# 原因在于,我们写的文件是要放到服务器里面的,服务器里的程序是不会停的,所以"打开文件"这个资源是不会被释放的,所以会持续的**占用程序内存**,影响服务器性能。
# 所以必须**手动关闭**文件,
# file_obj.close()  这个手动关闭的方式并不是我们常用的方式,会有点小麻烦。因为,close()之后,想继续往文件里写东西是会报错的。

**所以更好的方式是使用 with ... as 语句 专门为打开文件来设置的。**
with open(file_name) as file_obj :   **调用open()后,将一个表示文件及其内容的对象存储到了变量**file_obj **中**

#     # 冒号:下面就是代码块
#     # 在with语句中可以直接使用file_obj来做文件操作
#     此时这个文件只能在with中使用,一旦with结束则文件会自动close()
#     print(file_obj.read())
# #如果在代码块外面再使用print(file_obj.read()),就会报错。

#接下来进一步的优化下,**下面基本就是处理文件的标准格式。**
file_name = 'hello'

try:  #使用try自动捕获错误
    with open(file_name) as file_obj :
        print(file_obj.read())
except FileNotFoundError: #捕获到FileNotFoundError错误,就打印提示线下
    print(f'{file_name} 文件不存在~~')

#我执行了,输出结果是:“hello 文件不存在~~”

06 文件的读取.py     读取大文件的常用方式1:

简单说,就是文件太大了,一次读取不完,就采取分块读取的方式,这样性能会稍微好点:


demo2.txt:
    锄禾日当午
    汗滴禾下土
    谁知盘中餐
    粒粒皆辛苦

file_name = 'demo2.txt'

try:
    # 调用open()来打开一个文件,可以将文件分成两种类型
    # 一种,是纯文本文件(使用utf-8等编码编写的文本文件)
    # 一种,是二进制文件(图片、mp3、ppt等这些文件)
    # open()打开文件时,默认是以文本文件的形式打开的,但是open()默认的编码为None
    #   所以处理文本文件时,必须要指定文件的编码
    with open(file_name,**encoding='utf-8'**) as file_obj:
        # 通过 read() 来读取文件中的内容
          如果直接调用read()它会将文本文件的所有内容**全部**都读取出来
          如果要读取的文件较大的话,会**一次性**将文件的内容加载到内存中,容易导致内存泄漏
        # 所以对于较大的文件,**不要直接调用read()**
        # help(file_obj.read) 可以看下.read的help信息
        # read()可以接收一个size作为参数,该参数用来指定要读取的字符的数量
        # 默认值为-1,它会读取文件中的所有字符
            **可以为size指定一个值,这样read()会读取指定数量的字符,**
        #       每一次读取都是从上次读取到位置开始读取的
        #       如果字符的数量小于size,则会读取剩余所有的
        #       如果已经读取到了文件的最后了,则会返回''空串
        # content = file_obj.read(-1)
        content = file_obj.read(6) 读6个字符,不是字节
        content = file_obj.read(6) 记录上次读取的位置,接着开始读。
        content = file_obj.read(6)
        content = file_obj.read(6)
        # print(content)
        # print(len(content))  显示有23个字符。有3个换行字符。
except FileNotFoundError :
    print(f'{file_name} 这个文件不存在!')

# 读取大文件的方式。也是不断的读取部分内容,不断的读取部分内容这样去读取文件
file_name = 'demo.txt'

try:
    with open(file_name,encoding='utf-8') as file_obj:
        **# 定义一个变量**file_obj**,来保存文件的内容**
        file_content = ''
        # 定义一个变量,**来指定每次读取的大小**
        chunk = 100
        # 创建一个循环来读取文件内容,**通过死循环,不断的读取内容**
        while True:   
            # 读取chunk大小的内容
            content = file_obj.read(chunk)
            # 检查是否读取到了内容
            if not content:  设置一个停止条件。没有获取到内容,返回的是**空字符串,而空字符串默认会转换成false。not false 就是true的意思。**
                # 内容读取完毕,退出循环 **true就是进入判断条件了。**
                break

            # 输出内容
            # print(content,end='')   print有个习惯,每次执行结束后,会默认加一个换行。加上end=''空串。**这样就不会每次print后把换行字符去掉。**
            file_content += content

except FileNotFoundError :
    print(f'{file_name} 这个文件不存在!')

print(file_content)

07 文件读取.py  文件读取3个方法:

import pprint   因为print结束后会自带\n,所以调用这个。或者在print中加上end=''。
import os
file_name = 'demo.txt'

with open(file_name , encoding='utf-8') as file_obj:
    **readline()**
      **该方法可以用来读取一行内容**
    # print(file_obj.readline(),end='')
    # print(file_obj.readline())
    # print(file_obj.readline())

    **readlines()**
      **readlines()从文件中读取每一行,并将其存储在一个列表中。在with代码块外,我们依然可以使用这个变量**
    # r = file_obj.readlines() # **从文件中读取每一行,并将其存储在一个列表中。该列表被存储到变量r中。在with代码块外,我们依然可以使用这个变量**
    # pprint.pprint(r[0])
    # pprint.pprint(r[1])
    # pprint.pprint(r[2])

    for t in file_obj:  这个循环,也是一行一行的读取内容
        print(t)

示例:

# 打开文件
file_name = r'./hello/demo2.txt'

# 调用open()来打开文件
file_obj = open(file_name)

try:
    with open(file_name,encoding='utf-8') as file_obj:
        content = file_obj.readlines()  #**['锄禾日当午\n', '汗滴禾下土\n', '谁知盘中餐\n', '粒粒皆辛苦']**
        print(content)
except :
    print(f'{file_name}这个文件不存在!')

08 文件的写入.py

file_name = 'demo5.txt'

# 使用open()打开文件时**必须要指定打开文件所要做的操作(读、写、追加)**
# 如果不指定操作类型,则默认是 读取文件 , 而读取文件时是不能向文件中写入的,没有权限
r 表示只读的
w 表示是可写的,使用w来写入文件时,如果文件不存在就会创建文件,如果文件存在则会截断文件
   截断文件指删除原来文件中的所有内容

a 表示追加内容,如果文件不存在会创建文件,如果文件存在则会向文件中追加内容
x 用来新建文件,如果文件不存在则创建,存在则报错
+ 为操作符增加功能
#   r+ 读取和写入,但是文件不存在时会提示报错
#   w+
#   a+
# with open(file_name , 'w' , encoding='utf-8') as file_obj:
# with open(file_name , 'r+' , encoding='utf-8') as file_obj:
with open(file_name , 'x' , encoding='utf-8') as file_obj:
    # 使用文件对象的方法write()将一个字符串写入文件
    # 如果操作的是一个文本文件的话,Python只能将字符串写入文本文件。如果类型不是字符串,需要str()做一个类型转换
    # 该方法会可以分多次向文件中写入内容
    # 写入完成以后,该方法会返回写入的字符的个数
    file_obj.write('aaa\n') # \n表示换行
    file_obj.write('bbb\n')
    file_obj.write('ccc\n')
    r = file_obj.write(str(123)+'123123\n')
    r = file_obj.write('今天天气真不错')
    print(r)

- END -

本文为原创文章

❤:在这里跟我一起学习技术、职场、人生、原理、健身、摄影、生活等知识吧!

❤: 欢迎点个关注一起学习,进步充实人生。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值