当前位置: 首页 > 工具软件 > python-magic > 使用案例 >

Python Magic——类

谯皓君
2023-12-01


前言

类,作为一个比较大的整体,大规模程序的编写难免少得了类的使用,相当于一个大包,大包里包括各部分的代码,类之间通过继承来联系起来,通过封装进行打包,形成一个整体。


一、三大编程范式

1. 面向过程编程

2. 函数式编程

3. 面向对象编程

① 面向对象的优点

a. 明确内外
b. 通过继承和多态在语言层面支持归一化的设计

② 常用术语

a. 抽象实现 b. 封装/接口 c. 组合(合成)
d. 派生(子类出新的特性)/继承/继承结构
e. 泛化(子类与父类相同特点)/特化(子类自定义,与父类不同)
f. 自省/反射

二、类的相关知识

① 声明函数 def fun(args): 函数体
② 声明类 class 类名: 类体 (class Chinese:pass)
③ 实例化 p1 = Chinese()
④ 分类 新式类Chinese(object): 经典类Chinese:
⑥ 属性 数据属性(函数外) 与 函数属性(函数内)
⑦ 查看属性字典 p = Chinese.dict()
⑧ 查看属性名称 Chinese.dict[‘name’] or p.name

三、具体属性介绍

1. 实例化

import time
t = time.asctime()
class School:
    def __init__(self,name,address,type):
        self.name = name
        self.address = address
        self.type = type

    def eaxm(self):
        print('%s学校正在考试' %self.name)

    def zhaosheng(self):
        print('%s%s学校正在%s招生,时间%s' %(self.name,self.type,self.address,t))

school = School('清华大学','北京','公立')  #实例化
school.eaxm()
school.zhaosheng()

2. 类的属性数据属性

# 类名
print('类名:',School.__name__)
# 类的文档字符串
print('类的文档字符串:',School.__doc__)
# 类的第一个父类
print('类的第一个父类:',School.__base__)
# 类的属性
print('类的属性:',School.__dict__)
# 类定义所在的模块
print('类定义所在的模块:',School.__module__)

3. 数据属性

# 增
School.add = 'add'
# 删
del School.eaxm
# 改
School.eaxm = 'eaxms'
# 查
print(School.eaxm)
print(School.add)

4. 实例属性

# 查
print(school.name)
print(school.address)

# 改
school.name = '北京大学'
print('类的属性:',School.__dict__)
print(school.name)

# 删
school.zhaosheng()
del school.name
# school.zhaosheng()  name属性值已经删除,无法再调用函数

5. 类与实例同时变化,互不影响

6. 静态属性

class Room:
    tag = 1
    def __init__(self,name,width,length,height,owner):
        self.name = name
        self.width = width
        self.length = length
        self.height = height
        self.owner = owner
    @property  # @property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。
    def cal_area(self):
        return self.width*self.height

    @classmethod  # classmethod修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
    # 默认自动传一个参数cls
    def tell_info(cls):
        print(cls)
        print('--->',cls.tag)

    # 可以自己添加参数,后面调用需自己传入数据
    def tell_info1(cls,x):
        print(cls)
        print('--->', x)

    @staticmethod  #返回函数的静态方法
    # 静态方法只是名义上的归属类管理,不能使用类变量和实例变量,是类的工具包
    def wash(a,b,c):
        print('%s,%s,%s正在洗漱' %(a,b,c))

p = Room('房子',100,100,1000,'alex')
print(p.cal_area)
p.tell_info()
p.tell_info1(10)
# 两种调用方法
Room.wash('a','b','c')
p.wash('a','b','c')

四、面向对象的三大特征

1. 继承

① 父类实例

class Father:
    money = 10
    def __init__(self,name):
        print('father')
        self.name = name
    def hit_son(self):
        print('%s正在看儿子' %self.name)
class Mother:
    money = 100
    def __init__(self,name):
        print('mother')
        self.name = name
    def kid_son(self):
        print('%s正在看儿子' %self.name)

② 单继承

class Son(Father):
    money = 20
    def __init__(self,name,age):
        self.name = name
        self.age = age

s1 = Son('alex',5)
# 父类与子类含同一属性时
# 调用子类信息
print(s1.money,s1.name)
# 调用父类信息
print('父类信息:',Father.money)

③ 多继承

class Son1(Father,Mother):
    money = 30
    def __init__(self,name,age):
        self.name = name
        self.age = age
s2 = Son1('kim',6)
# 父类与子类含同一属性时
# 调用子类信息
print(s2.money,s2.age)
# 调用父类信息
print('父类1信息:',Father.money,'父类2信息:',Mother.money)


a. 子类继承父亲的所有属性,若父亲与子类重名,不存在覆盖这一说,先找子类的,再找父类的
b. 方法改写,以本身类为准

④ 多继承接口继承(归一化)

# 子类继承父类,子类必须有父类方法才可实例化
import abc
class All_file(metaclass=abc.ABCMeta):
    def read(self):
        pass
    def write(self):
        pass
class Disk(All_file):
    def read(self):#必须有
        print('Disk')
    def write(self):#必须有
        print('Disk')
p = Disk()
p.write()
p.read()
# 接口(函数)继承,定义一个基类,基类中的方法定义成接口函数,只要来一个子类继承他,那子类必须实现这个方法
# 接口类方法不用去实现,不用实例化,只是规范

⑤ 继承顺序

a. python2—深度优先
b. python3—广度优先
c. MRO列表,存放继承顺序
print(Son.mro) #基类(最小的)放在最后

⑥ 三准则

a.子类会先于父类被检查
b.多个父类会根据他们在列表中的顺序被检查
c.如果对下一个类存在两个合法的选择,选择第一个父类

⑦ 继承实例

class Vehicle:
    country = 'China'
    def __init__(self,name,speed,load,power):
        self.name = name
        self.speed = speed
        self.load = load
        self.power = power
    def run(self):
        print('开动了')

class Subway(Vehicle):
    def __init__(self,name,speed,load,power,line):
        # Vehicle.__init__(self,name,speed,load,power)
        super().__init__(name, speed, load, power)  # super调用父类方法,与上等同
        self.line = line


    def show_info(self):
        print(self.name,self.line)

    def run(self):
        # Vehicle.run(self)
        super().run()  # super调用父类方法,与上等同
        print('%s开动啦' %self.name)

line = Subway('北京线','10km/h',10000,'电',12)
line.show_info()
line.run()

⑧ 什么时候使用继承

a. 当类之间显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。
b. 当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好。

2. 多态

调用相同的父类,运用相同的方法,得到不同的结果,多态实质上是继承的实现细节。

多态实例

class H2O:
    def __init__(self,name,tem):
        self.name = name
        self.tem = tem
    def turn_ice(self):
        if self.tem<0:
            print('[%s]温度太低,结成了冰' %self.name)
        elif self.tem>=0 and self.tem<=100:
            print('[%s]温度正常,状态为水' %self.name)
        else:
            print('[%s]温度太高,形成水蒸气' %self.name)

class Water(H2O):
    pass
class Ice(H2O):
    pass
class Steam(H2O):
    pass

w1 = Water('水',25)
w1.turn_ice()
i1 = Ice('水',-12)
i1.turn_ice()
s1 = Steam('水',120)
s1.turn_ice()

3. 封装

在另一个文件中,使用import调用,封装的本质就是将用户用不到的属性进行隐藏(可以使用接口函数来调用)

封装的层面

a. 第一层面的封装:类就是麻袋,这本身就是一种封装。
b. 第二层面的封装:类中定义私有的,只在类的内部使用,外部无法访问。
c. 第三层面的封装:明确区分内外,内部实现逻辑,外部无法实现。并且为封装到内部的逻辑提供一个访问接口给外部使用(真正意义)。

4.关系

类的继承有两层意义:改变 扩展,多态就是类的这两层意义的一个具体的实现机制。即:调用不同的类实例化得到对象下的相同方法,实现的过程不一样,Python中的标准类型就是多态概念的一个很好的示范

5. 自省/反射

class test:
    status = 'abc'
    def __init__(self,name,tem):
        self.name = name
        self.tem = tem
    def turn_ice(self):
        if self.tem<0:
            print('[%s]温度太低,结成了冰' %self.name)
        elif self.tem>=0 and self.tem<=100:
            print('[%s]温度正常,状态为水' %self.name)
        else:
            print('[%s]温度太高,形成水蒸气' %self.name)

c = test('水',120)
print(c.name,c.tem)

① hasattr(实例,属性名) #判断是否存在

print(hasattr(c,'name'))

② getattr(实例,属性名,默认值) #返回对应的属性值;如没有该属性,返回后面给定的默认值

print(getattr(c,'name'))
print(getattr(c,'nam','abc'))

反射原理,与if-else,字典存储函数名调用一样,但反射更为简单

if hasattr(c,‘turn_ice’):
getattr(c,‘turn_ice’)()

③ setattr(实例,key,value) #将数据插入数据字典

import time
setattr(c,'times',time.asctime())
print(c.__dict__)

④ delattr(实例,属性名) #删除属性

delattr(c,'tem')
print(c.__dict__)

⑦ 反射原理,与if-else,字典存储函数名调用一样,但反射更为简单

if hasattr(c,'turn_ice'):
    getattr(c,'turn_ice')()

⑦ 自定义

p = Foo(100)
# 调用不存在的参数,执行__getattr__
p.sss
# 调用存在的参数,不执行__getattr__
p.y

# 删除已有属性时,执行__delattr__方法;若属性没有则会报错
print(p.__dict__)
del p.y
print(p.__dict__)

# 赋值修改时执行__setattr__
p1 = Foo(120)

⑧ 授权

import time
class Open:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file = open(filename,mode,encoding=encoding)
        self.mode = mode
        self.encoding = encoding

    # 重新授权一种新的write方式,具有新的格式(定制)
    def write(self,line):
        t = time.strftime('%Y-%m-%d %x')
        self.file.write('%s %s' %(t,line))

    # 如果没有函数则放开所有权限(通用性)
    def __getattr__(self, item):
        return getattr(self.file,item)
f = Open('a.txt','w+')
f.write('cpu负载\n')
f.write('内存不足\n')

⑨ isinstance(obj,cls) 与 issubclass(sub,super)

# isinstance(obj,cls)  判断obj是否为cls的实例,跨族谱也可以
print(isinstance(f,Open))

# issubclass(sub,super)  判断sub是否为super的子类
class Foo:
    pass
class Goo(Foo):
    pass
print(issubclass(Goo,Foo))

getitemsetitem 与 __delitem__迭代器协议

class Foo:
    def __getitem__(self, item):
        print('getitem')
        return self.__dict__[item]
    def __setitem__(self, key, value):
        print('setitem')
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('delitem')
        self.__dict__.pop(key)

# 只有按照字典方法才可显示提示信息

# 改变对象的字符串显示
class Foo:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __str__(self):
        return '姓名%s,年龄%s' %(self.name,self.age)

f = Foo('Li',12)
print(f)
# 1.平常,只会显示f是Foo实例得来的,其他无任何信息
# 2.加__str__,可以定义显示内容,无则为默认__str__,,如平常显示

⑪ 迭代器协议

# 迭代器协议
class Foo:
    def __init__(self,n):
        self.n = n
    def __iter__(self):
        return self
    def __next__(self):
        if self.n == 16:
            raise StopIteration('终止')
        self.n+=1
        return self.n

f = Foo(12)
print(f.__next__())
next(f)
print(next(f))
f.__next__()
print(next(f))

⑪ 斐波那契数列

# 斐波那契数列(相邻两个数相加等于第三个数)
class Fib:
    def __init__(self):
        self.a = 1
        self.b = 1
    def __iter__(self):
        return self
    def __next__(self):
        if self.a > 12:
            raise StopIteration('终止')
        self.a,self.b = self.b,self.a+self.b
        return self.a

f = Fib()
print(f.__next__())
print(next(f))
print('---')
for i in f:
    print(i)

⑫ 描述符

简要概述

# 实质上是一个新式类,至少实现了__get__(),__set__(),__delete__()
# __get__()调用属性触发
# __set__()为一个属性赋值时触发
# __delete__()采用del删除属性时触发
# 包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法(在实例调用中才可触发)
class Foo:
    def __get__(self,instance,owner):
        print('触发get')
    def __set__(self,instance,owner):
        print('触发set')
    def __delete__(self, instance):
        print('触发delete')

p = Foo()
p.name = 'abc'
p.name
del p.name

描述符str

# 为描述符的对象的类属性进行服务
class Str:
    def __get__(self, instance, owner):
        print('Str调用...')
        print(instance, owner)

    def __set__(self, instance, value):
        print('Str设置...')
        print(instance, value)

    def __delete__(self, instance):
        print('Str删除...')
        print(instance)
        
class People:
    name = Str()  # name 属性被代理,将这个类作用于另外一个类的属性来使用
    def __init__(self, name):  # name被Str类代理
        self.name = name

p1 = People('long')
# 描述符Str的使用
p1.name
p1.name = 'sss'
del p1.name

print(p1.__dict__)
print(People.__dict__)

描述符种类

# 描述符分为两种
# 1.数据描述符,至少实现__get__和__set__
class Foo:
    def __set__(self, instance, value):
        print('set')
    def __get__(self, instance, owner):
        print('get')

# 2.非数据描述符,没有__set__的实现
class Foo:
    def __get__(self, instance, owner):
        print('get')

# 1.描述符本身应该定义成新式类,被代理的类也应该是新式类
# 2.必须把描述符定义成这个类的类属性,不能定义到构造函数中
# 3.要严格遵循该优先级,优先级由高到底:类属性>数据描述符>实例属性>非数据描述符>找不到的属性触发__getattr__()

总结

类这个大的整体在各个程序中都普遍被使用,而且是代码显得比较规整,看起来逻辑比较清晰明了,简单来说就是重要的一批。

 类似资料: