二、面向对象

1 类的基本使用

class Person:
    def __init__(self, name):
        self.name = name
  
    def info(self):
        print(f'姓名为{ self.name }')

person = Person('小明')
person.info()  # 姓名为小明

python3中写成class 类名会自动解析为新式类class 类名(object)

2 类中的self(代表调用对象自身)

class Person:
    def info(self):
        print(self)

person = Person('小明')
print(person)  # <__main__.Person object at 0x000001ADE83CAE48>
person.info()  # <__main__.Person object at 0x000001ADE83CAE48>

3 魔法方法

class Washer:
    def __init__(self, w, h):
        self.width = w
        self.height = h

    def __str__(self):
        return '洗衣机对象'

    def __del__(self):
        print(f'【{self}】已被删除')

washer = Washer(100, 200)
print(washer)
# 控制台输出如下:
#  洗衣机对象
#  【洗衣机对象】已被删除

4 私有属性和方法

class Person:
    def __init__(self):
        self.__age = 17

    def __priv(self):
        print('Person私有方法')

class Son(Person):
    pass

person = Person()
print(person.__age)  # 报错
print(person._Person__age )  # 17
person.__priv()  # 报错
person._Person__priv()  # Person私有方法

son = Son()
print(son.__age )  # 报错
print(son._Son__age )  # 报错
print(son._Person__age )  # 100

son.__priv()  # 报错
son._Son__priv()  # 报错
son._Person__priv()  # Person私有方法

5 继承

5.1 多继承

class A:
    def info(self):
        print(f'A中的info方法')

class B:
    def info(self):
        print(f'B中的info方法')

class Test(B, A):
    pass

test = Test()
print(Test.__mro__)
test.info()
# 控制台输出如下:
# (<class '__main__.Test'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
# B中的info方法

5.2 子类调用父类方法

class Animal:
    def __init__(self):
        self.name = 'animal'

    def info(self):
        print(f'Animal中{self.name}')


class Dog(Animal):
    def __init__(self):
        self.name = 'dog'

    def info(self):
        # 调用子类初始化方法,对象name属性值赋为dog
        self.__init__()
        print(f'Dog中{self.name}')

    def super_info(self):
        # 调用父类初始化方法,对象name属性值赋为animal
        super().__init__()  # super(Dog, self).__init__()的缩写,也可写为Animal.__init__(self)
        super().info()  # super(Dog, self).info()的缩写,也可写为Animal.info(self)


dog = Dog()
dog.info()
dog.super_info()
dog.info()
# 控制台输出如下:
# Dog中dog
# Animal中animal
# Dog中dog

若子类Dog调用完父类方法后不做self.__init__()初始化,即为如下代码:

class Dog(Animal):
    def __init__(self):
        self.name = 'dog'

    def info(self):
        # self.__init__()
        print(f'Dog中{self.name}')

    def super_info(self):
        super().__init__()
        super().info()

此时按照如上调用顺序则控制台输出为(对象name属性被super().__init__()初始化为animal):

# Dog中dog
# Animal中animal
# Dog中animal

6 多态

class Animal:
    def bark(self):
        pass

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

class Cat(Animal):
    def bark(self):
        print('喵喵喵~')

class Test:
    def bark(self, obj):
        obj.bark()

dog = Dog()
cat = Cat()
test = Test()
test.bark(dog)  # 汪汪汪~
test.bark(cat)  # 喵喵喵~

多态:继承自同一父类的不不同子类,实例化出不同对象,传入不同对象调用相同方法表现出不同状态

7 类属性、类方法和静态

7.1 类属性

class Test:
    a = 100

test = Test()
print(Test.a)  # 100
print(test.a)  # 100
test.a = 200  # 此处并非是将类属性a值改为200,而是实例test新增一个属性a,值为200
print(Test.a)  # 100
print(test.a)  # 200

7.2 类方法

class Test:
    __age = 100

    def __init__(self):
        self.__age = 20
    
    @classmethod
    def get_age(cls):
        print(cls.__age)

    def instance_fun(self):
        print('实例方法')

test = Test()
print(test.__age)  # 报错
print(test._Test__age)  # 20 此处访问的是实例私有属性
print(Test.__age)  # 报错
print(Test._Test__age)  # 100 此处访问的是类私有属性
test.get_age()  # 100 此处访问类属性而非实例属性
Test.get_age()  # 100
test.instance_fun()  # 实例方法
Test.instance_fun()  # 报错,类不能访问实例方法
\ 实例方法 类方法 静态方法
a = A() a.foo(x) a.class_foo(x) a.static_foo(x)
A 不可用 A.class_foo(x) A.static_foo(x)

7.3 静态方法

class Test:
    @staticmethod
    def static_fun():
        print('静态方法')

test = Test()
test.static_fun()  # 静态方法
Test.static_fun()  # 静态方法

8 异常

8.1 基本使用

try:
    可能发生异常的代码
except:
    出现异常执行的代码

8.2 详细语法

try:
    可能发生异常的代码
except 异常类型 as result:
    print(result)  # 捕获单个异常并输出异常信息
except (异常类型1,  异常类型2) as result:
    print(result)  # 捕获多中可能出现的异常并输出具体发生的某一异常信息
except:
    捕获所有异常
except  Exception as result:
    print(result)  # 捕获所有可能出现的异常并输出具体发生的某一异常信息
else:
    没有发生异常时执行的代码
finally:
    无论是否发生异常都执行的代码

8.3 自定义异常

class Error(Exception):
    def __init__(self, len, min_len):
        self.len = len
        self.min_len = min_len

    def __str__(self):
        return f'输入的长度为{self.len},不能少于{self.min_len}个字符'

try:
    con = input('请输入密码:')
    if len(con) < 3:
        ex = Error(len(con), 3)  # 也可直接不定义专门异常类,ex = Exception(输入密码长度小于3位)
        raise ex
except Exception as result:
    print(result)
else:
    print('密码输入完成')

由于异常具有传递性,所以一般在主程序中进行异常捕获即可,无需在每个子函数中进行处理

9 模块

9.1 模块导入

"""
方法一:
导入:import 模块名(import 模块名1, 模块名2...)
使用:模块名.功能
"""
import math
print(math.sqrt(9))

"""
方法二:
导入:from 模块名 import 功能(from 模块名 import 功能1, 功能2...)
使用:功能调用(不需要书写模块名.功能)
"""
from math import sqrt
print(sqrt(9))

"""
方法三:
导入:from 模块名 import *
使用:功能调用(不需要书写模块名.功能)
"""
# 方法三:from 模块名 import *; 功能调用(不需要书写模块名.功能)
from math import *
print(sqrt(9))

"""
方法四:
导入:import 模块名 as 模块别名
使用:模块别名.功能
"""
import time as tt
tt.sleep(2)
time.sleep(2)  # 报错,使用了模块别名就不能再用原模块名了
print('hello')

"""
方法五:
导入:from 模块名 import 功能 as 功能别名
使用:功能别名调用(不需要书写模块名.功能别名)
"""
from time import sleep as sl
sl(2)
sleep(2)  # 报错,使用了功能别名就不能再用原功能名了
print('hello')

导入模块时会自动执行模块中全局作用域下的代码,因此模块中的测试代码需写在如下区域,才能保证在导入该模块时其中的测试代码不被执行:

if __name__ == '__main__':
   测试代码

9.2 模块定位

python解析器对模块位置的搜索顺序为:

  • 1 当前目录
  • 2 系统目录
# python中每一个模块都有一个内置属性__file__,可以查看到导入模块的完整路径
import random
print(random.__file__)  # D:\Software\Python\lib\random.py

若同一文件导入的多个模块中存在同名的功能,后导入的模块功能会覆盖其之前导入的模块功能

9.3 __all__列表(指定要暴露给外界的模块功能列表)

9.3.1 不指定__all__列表

  • 模块文件test.py
def test1():
    print('test1')

def test2():
    print('test2')
  • 导入模块
# 方法一
import test
test.test1()  # test1
test.test2()  # test2

# 方法二
from test import test1, test2
test1()  # test1
test2()  # test2

# 方法三
from test import *
test1()  # test1
test2()  # test2

9.3.2 指定__all__列表

  • 模块文件test.py
__all__ = ['test1']
def test1():
    print('test1')

def test2():
    print('test2')
  • 导入模块
# 方法一
import test
test.test1()  # test1
test.test2()  # test2

# 方法二
from test import test1, test2
test1()  # test1
test2()  # test2

# 方法三
from test import *
test1()  # test1
test2()  # 报错,因为通过all列表指定了允许导入的模块功能只有test1

9.4 模块中的单下划线全局变量

若某一py模块文件内容中存在单下划线全局变量,使用from 模块名 import *导入模块后访问该变量会报错(类中只有双下划线开头的属性才算私有属性,定义单下划线的属性不算私有属性)

  • module.py模块内容
aa = 10
_bb = 20
  • 导入module.py模块
from module import *
print(aa)  # 10
print(_bb)  # 报错

from module import _bb
print(_bb)  # 20

import module
print(module._bb)  # 20

9.5 模块导入注意点

若一个模块文件中有一个全局变量,则其他文件中使用import 模块名from 模块名 import 全局变量导入模块意义不同

  • module.py模块内容
G_NUM = 0
G_LIST = [1, 2]

9.5.1 import 模块名

import 模块名导入时,模块名指向公共模块,使用模块名.全局变量指向的是公共模块中的全局变量名,对其赋值相当于在公共模块中给全局变量修改值

  • deal1.py内容
import module
def deal1():
    module.G_NUM = 1
    module.G_LIST.append(3)

def print1():
    print(module.G_NUM)
    print(module.G_LIST)
  • deal2.py内容
import module
def print2():
    print(module.G_NUM)
    print(module.G_LIST)
  • main.py内容
import deal1
import deal2

deal1.deal1()
deal1.print1()  # 1 [1, 2, 3]
deal2.print2()  # 1 [1, 2, 3]

9.5.2 from 模块名 import 全局变量

from 模块名 import 全局变量导入时,全局变量指向公共模块中全局变量指向的内存地址,对其赋值相当于让该模块中全局变量重新指向一块新的内存地址,不影响公共模块中的值,但全局变量为可变数据类型时,使用方法修改值,因为内存地址不变,公共模块中的可变数据类型变量也指向该地址,所以公共模块中的可变数据类型变量值也会变

  • deal1.py内容
from module import G_NUM, G_LIST
def deal1():
    global G_NUM
    G_NUM = 1
    G_LIST.append(3)

def print1():
    print(G_NUM)
    print(G_LIST)
  • deal2.py内容
from module import G_NUM, G_LIST
def print2():
    print(G_NUM)
    print(G_LIST)
  • main.py内容
import deal1
import deal2

deal1.deal1()
deal1.print1()  # 1 [1, 2, 3]
deal2.print2()  # 0 [1, 2, 3]

10 包

python新建包会在包目录下自动生成一个__init__.py文件,若my_package包下有两个py模块文件my_package1.py和my_package2.py,即可在__init__.py文件中通过all列表写入指定要暴露给外界使用的模块

  • __init__.py文件内容
__all__ = ['my_package1']
  • 导入包
# 方法一
import my_package
my_package.my_package1.方法名()  # 报错
my_package.my_package2.方法名()  # 报错

# 方法二
import my_package.my_package1, my_package.my_package2
my_package.my_package1.方法名()  # 正确调用
my_package.my_package2.方法名()  # 正确调用

# 方法三
from my_package import my_package1, my_package2
my_package1.方法名()  # 正确调用
my_package2.方法名()  # 正确调用

# 方法三
from my_package import *
my_package1.方法名()  # 正确调用
my_package2.方法名()  # 报错,因为__init__.py文件中all列表指定了允许导入的模块只有my_package1

11 __dict__方法

class Test:
    def __init__(self):
        self.name = 'zhangsan'
        self.tel = 123456

test = Test()
print(test.__dict__)  # {'name': 'zhangsan', 'tel': 123456}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容