Python(5):类(魔术方法+反射方法+静态属性+封装/继承/多态)

一、认识类

举一个简单的例子:把人看成一个类,人有很多共同的属性(年龄,姓名,身高等待),人有很多共同的方法(去吃饭,去睡觉,去看蔡徐坤跳舞等等)。而李明就是人的一个对象,他有人(类)的属性和方法。

1.定义一个简单的类及其使用

在Python中,类通过 class 关键字定义,类名通用习惯为首字母大写,Python3中类基本都会继承于object类,语法格式如下,我们创建一个People类:

# -----------------------方式1
# # 创建People类,People为类名,继承object类也可以不继承object类;两者区别不大,但没有继承于object类使用多继承时可能会出现问题。
class People(object):  
    pass

# -----------------------方式2
# 默认继承object类
class People:
    pass

有了People类的定义,就可以创建出具体的实例.如下我们创建两个People类的实例,并且使用类中的方法和公共属性

class People:
    def __init__(self):
        self.China = "中国"

    def chifan(self):
        print('我要吃饭')

    def wc(self):
        print('我要上厕所')


if __name__ == '__main__':
    p1 = People()
    p2 = People()

    p1.chifan()  # 我要吃饭
    p1.wc()  # 我要上厕所

    print(p2.China)  # 中国
    p2.chifan()  # 我要吃饭

我们看到刚刚上述,每个方法里有一个self(名字可改,默认self),该值是类中的方法需要的,哪个对象调用方法或者属性,self就是那个值,如果在类中的某个方法调用类中的另一个方法或属性,可以直接使用

class People:
    def __init__(self):
        self.China = "中国"

    def chifan(self):
        print('我要吃饭')

    def wc(self):
        print('我要上厕所')

    def aaa(self):
        print(self.China)
        self.chifan()
        self.wc()


if __name__ == '__main__':
    p = People()
    p.aaa()

在这里插入图片描述

2.类属性和实例属性

class People:
    father = '张三'

    def fun(self, mother):
        return '爸爸是{},妈妈是{}'.format(self.father, mother)

if __name__ == '__main__':
    people = People()
    print(people.fun('丽丽'))
    print(id(people.father))  # 类属性可以通过对象名.变量名
    print(id(People.father))  # 也可以用类名.变量名

    people.father = '李四'  # 实例属性只修改自己的值,与原来的值无关,类似一个局部变量
    People.father = '王五'  # 会修改整个类的值,但是有的对象通过对象名.变量修改过值的,无法再修改,因为此时地址已经不同
    print(people.father)
    print(People.father)

在这里插入图片描述

二、面向对象的三大特征

1.封装

  • 定义: 封装是将类中的某些方法或者属性隐藏,对象不能直接使用隐藏的方法或者属性,具有保护功能
  • 格式: __属性(方法),使用双下划线
  • 目的: 保护隐私
  • 调用: 只有在本类的内部可以使用,外部不可以修改和使用

举例:

class Girl:
    name = '张三'
    __age = 15

    def show(self):
        print(self.name, self.__age)


if __name__ == '__main__':
    g = Girl()
    g.show()

当我们在类中调用时候,是正常的
在这里插入图片描述
如果实例直接调用呢?因为是私有属性,外部不可直接使用,会报错

print(g.age)
# 或者
print(g.__age)

在这里插入图片描述
那么我们直接修改这个__age呢?

g = Girl()
g.__age = '20'
print(g.__age)
  • 这样使用并不是修改类中的__age,而是重新创建了一个__age的属性

  • 另外我们可以使用实例对象.__dict__方法查看所有a属性的元素

    print(g.__dict__)#---{'name': '张三', '_Girl__age': 10, '__age': 15}
    
  • 但是如果非想改,可以使用实例对象._类名__属性名修改,但是不建议使用

    g = Girl()
    g._Girl__age = 30
    g.show()
    

    在这里插入图片描述

2.继承

  • 定义: 子类需要使用父类的属性和方法,但是子类中也可以定义自己的属性和方法。
  • 概念: 被继承的类叫父类/基类/超类,继承的类叫子类/派生类
  • 注意事项
    1. 子类继承父类,如果子类不重写父类的某个方法,那么子类执行父类的方法
    2. 子类继承父类,如果子类复写了某个方法,那么子类只使用自己的方法
    3. 父类的私有属性不能被继承

① 单继承

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def eat(self):
        print('吃')

    def sleep(self):
        print('睡')

class Cat(Animal):#-------------------继承谁就在括号里写谁
    def jiao(self):
        print('喵')

class Dog(Animal):
    def jiao(self):
        print('汪')

if __name__ == '__main__':
	c = Cat('猫', 10)
	d = Dog('狗', 12)
	c.eat()#-----------------吃
	d.eat()#-----------------吃

查看父类的方法__bases__

print(Cat.__bases__)#(<class '__main__.Animal'>,)
print(Animal.__bases__)#(<class 'object'>,),object是所有类的组宗,这种类叫新式类

方法重写:当子类要使用的方法在父类中已经定义,但是和自己想用的方法有出入,那么就可以对父类的方法进行重写

class A():
    def hehe(self):
        print('呵呵')
class B(A):
    def hehe(self):
        print('哈哈')

if __name__ == '__main__':
	a=A()
	b=B()
	a.hehe() #呵呵
	b.hehe() #哈哈

子类和父类有相同的方法,如果子类调用父类相同的方法可以使用super方法

class Animal():
    def __init__(self,name):
        self.name=name
class Cat(Animal):
    def __init__(self,name,speed):
        self.speed=speed
        super().__init__(name)
    def jiao(self):
        print(self.name,self.speed)

if __name__ == '__main__':
	c=Cat('波斯猫',3500)
	c.jiao() 

# 结果:波斯猫  3500

② 多继承

多继承如果父类们拥有共同的方法则按照第一个顺序进行继承

class A:
    def shuchu(self):
        print('A')

class B():
    def shuchu(self):
        print('B')

class C(A, B):
    def shuchu3(self):
        print('C')

if __name__ == '__main__':
    c = C()
    c.shuchu()  

# 结果:A

多继承的顺序:我们使用mro方法,该方法可以根据广度优先算法,得出继承顺序,一步一步执行

class A():
    def func(self):
        print('A开始')
        print('A结束')

class B(A):
    def func(self):
        print('B开始')
        super().func()
        print('B结束')
        
class C(A):
    def func(self):
        print('C开始')
        super().func()
        print('C结束')
        
class D(B,C):
    def func(self):
        print('D开始')
        super().func()
        print('D结束')
        
d=D()
d.func()

class A:
    def __init__(self):
        print('A开始')
        print('A结束')
        
class B(A):
    def __init__(self):
        print('B开始')
        super().__init__()
        print('B结束')
        
class C(A):
    def __init__(self):
        print('C开始')
        super().__init__()
        print('C结束')
        
class D(B,C):
    def __init__(self):
        print('D开始')
        super().__init__()
        print('D结束')
        
# d=D()
print(D.mro())

"""
注意事项:
	1. 按此mro列表中的从左到右开始查找积累,直到找到第一个匹配的属性的类为止
    2.也就是多个父类,按照此列表的顺序被检查
    3.此类题不用init方法,用别的方法也是一样
"""

在这里插入图片描述

3.多态

多态指的是一类事物有多种形态,一个抽象类有多个子类(因而多态的概念依赖于继承),不同的子类对象调用相同的方法,产生不同的执行结果,多态可以增加代码的灵活度

实现多态的步骤:

  1. 定义一个父类(Base),实现某个方法(比如:run)
  2. 定义多个子类,在子类中重写父类的方法(run),每个子类run方法实现不同的功能
  3. 假设我们定义了一个函数,需要一个Base类型的对象的参数,那么调用函数的时候,传入Base类不同的子类对象,那么这个函数就会执行不同的功能,这就是多态的体现。

由于Python中函数的参数是没有类型限制的,所以多态在python中的体现并不是很严谨。多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。

这里举个例子使用:

class Animal(object):
    """动物类"""

    def func(self):
        print('动物发出了声音')


class Cat(Animal):
    """猫类"""

    def func(self):
        print('喵 喵 喵')


class Hero:
    def func(self):
        print('这个是英雄类的方法,不是动物类的对象')


def work(CLASS):
    CLASS.func()


work(Cat())  # Hero
work(Hero())  # 这个是英雄类的方法,不是动物类的对象

三、类的8个常见的魔术方法

1、__init__方法

  • 触发时间:在创建对象的时候自动执行
  • 作用:常用来定义一些初始化属性
class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def fun(self):
        print('我的名字是{}今年{}岁'.format(self.name, self.age))


if __name__ == '__main__':
    a = A('张三', 12)  # 传入方式,在实例对象时候按顺序传入,或者使用关键字参数方式传入
    a.fun()

在这里插入图片描述

2.__str__方法

  • 触发时间:在打印对象的名称时候调用,使用%s也是默认调用str
  • 作用:可以保存一些信息
  • 注意:使用return返回,必须返回字符串,自动打印return的信息
class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return '我的名字是{}今年{}岁'.format(self.name, self.age)


if __name__ == '__main__':
    a = A('张三', 12)
    print(a)

在这里插入图片描述

3.__repr__方法

  • 作用:改变对象的字符串显示
  • 触发时间:%r默认调用repr方法
  • 优先级:优先级低于str,找不到str方法才会找repr方法

不使用%r

class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return '现在是str方法'

    def __repr__(self):
        return '现在是repr方法'

if __name__ == '__main__':
	a = A('张三', 12)
	print(a)

在这里插入图片描述
使用%r时候

class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return '现在是str方法,我叫%s' % self.name

    def __repr__(self):
        return '现在是repr方法,我叫%s' % self.name

if __name__ == '__main__':
	a = A('张三', 12)
	print('%r'%a)
	print('%s'%a)

在这里插入图片描述
4.__del__方法

  • 触发时间:当一个对象在内存被销毁的时候自动执行,自动调用,无需手动调用
  • 无返回值
  • 作用:在对象销毁时可以做一些操作
class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __del__(self):
        print('执行我,代表此时需要把定义的对象删除了!')

if __name__ == '__main__':
	a = A('张三', 12)

在这里插入图片描述

5.__call__方法

  • 触发时间:例如a是一个对象,当a()时候,触发
  • 触发方式:对象名字后加()触发
class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __call__(self, *args, **kwargs):
        print('现在调用call方法')

if __name__ == '__main__':
	a = A('张三', 12)
	a()

在这里插入图片描述

6.__new__方法

  • 触发时间:实例化对象时候触发
  • 必须返回一个实例对象
  • 作用:实例化对象
  • 尽量少使用该方法,是底层object类实现,object是所有类的父类
class A:
    def __new__(cls, *args, **kwargs):
        print('我是一个类中最先执行的方法,我的作用是创建一个实例')
        return object.__new__(cls)

    def __init__(self):
        print('我是在new后执行的,我是初始化函数')

if __name__ == '__main__':
	a = A()

在这里插入图片描述

7.__eq__方法

①is和==的区别

  • is比较的是两个对象的id是否相等,是否指向同一个内存地址
  • ==比较的是两个对象的值是否相等,内存地址可以不同

②eq方法

class A:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name


if __name__ == '__main__':a = A('张三')
	b = A('张三')
	c = A('李四')
	print(id(a))  # 31105600
	print(id(b))  # 31105768
	print(id(c))  # 39288504
	print(a == b)  # True
	print(a == c)  # False
	print(a is b)  # False
	print(a is c)  # False

8.__hash__方法

①hash知识:哈希也叫散列。是将一串不定长的输入,转换为一种定长的输出

  • 不可逆的单项运算,并且生成的结果不会重复
  • 常见算法:SM3、MD5,SHA-1
  • 作用:保证了数据的唯一性
  • 用途:账号的密码
  • 注意:只有不可变类型才可以hash

python内置的hash算法:

在这里插入图片描述

②hash魔术方法

  • 只定义了eq方法,没定义hash方法,hash方法会隐式设置为none
  • 没有定义eq和hash,会继承object类中的方法
class A:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name

    def __hash__(self):
        return hash(id(self))

if __name__ == '__main__':
	a = A('张三')
	b = A('张三')
	c = A('李四')
	print(id(a))  # 31105600
	print(id(b))  # 31105768
	print(id(c))  # 39288504
	print(a == b)  # True
	print(a == c)  # False
	print(a is b)  # False
	print(a is c)  # False

四、类判断父类和子类的函数

1.issubclass():判断一个类是不是其他类的子类
格式:

 issubclass(被检测类,父类)   
 
issubclass(被检测类,(父类1,父类2,父类3...))

返回值为布尔值,只要符合一个条件是一个的父类,就会返回true

class A:
    pass

class B:
    pass

class C(A):
    pass


print(issubclass(C, A))  # true
print(issubclass(C, B))  # false
print(issubclass(C, (A, B))) # true

2.isinstance():检测一个对象是否为某个类的对象
格式:

isinstance(对象,类)   
isinstance(对象,(1,类2...))

返回值为布尔值,只要一个类是当前对象的类,返回True

class A:
    pass
    
class B:
    pass

a=A()
print(isinstance(a,A))
print(isinstance(a,B))
print(isinstance(a,(A,B)))

在这里插入图片描述

五、类中的反射方法

反射:通过字符串的形式操作对象相关的属性

函数解释
hasattr判断是否有此变量,返回bool值
getattr获取属性值或者获取方法变量的地址
setattr给类或者对象设置属性或者方法
delattr删除类或者对象的属性或者方法

1.getattr(对象,属性/方法,不存在时候返回的值)

获取属性或者方法,不存在会报错

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


a = A()
print(getattr(a, 'name'))
print(getattr(a, 'age', '无age属性'))
print(getattr(a, 'age'))#查不到该属性且无默认值会报错

在这里插入图片描述

2.hasattr(对象/类,成员名)

查询某个对象或类的成员是否存在

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

    def haha(self):
        print(123)


a = A()
print(hasattr(a, 'name'))
print(hasattr(a, 'age'))

在这里插入图片描述

3.setattr(对象,成员名,值)

设置或修改对象的值

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

    def haha(self):
        print(123)


a = A()
print(a.name)  # 张三
setattr(a, 'name', '蔡徐坤')
print(a.name)  # 蔡徐坤


4.delattr(对象,成员)

删除对象的某个值

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

    def haha(self):
        print(123)


a = A()
setattr(a,'age',12)
print(a.age)
delattr(a,'age')
print(a.age)

在这里插入图片描述

六、静态属性-property

可以把类中不带参数的方法,变成属性直接使用

class Student:
    @property
    def fun(self):
        print('鸡汤来喽...')


if __name__ == '__main__':
    s1 = Student()
    s1.fun

在这里插入图片描述

七、实例方法、静态方法、类方法的区别

python中有三种方法,分别是实例方法、静态方法(staticmethod) 和 类方法(classmethod)。

  • 实例方法只能通过实例对象调用,不能通过类进行调用。实例方法再定义时候使用关键字self,self代表实例对象本身。
  • 静态方法可以使用实例对象调用,也可以使用类进行调用,他的的特点没有参数限制,定义时需要在函数前加@staticmethod
  • 类方法可以被类调用,也可以被实例对象调用,实例调用可以给类增加属性,类的属性修改需要通过类进行修改,类方法需要使用关键字cls,定义时候需要在函数前加@classmethod

看一个示例

class A:
    def fun1(self):
        print('我是实例方法(对象方法),第一个参数是该对象')

    @classmethod
    def fun2(cls):
        print('我是类方法,第一个参数是类')

    @staticmethod
    def fun3(a, b):
        print('我是静态方法,我的参数和此类毫无关系,不写参数也可以')


if __name__ == '__main__':
    a = A()
    a.fun1()  # 我是实例方法(静态方法),第一个参数是该对象
    A.fun1(a)  # 可以用类调用对象方法吗?答案是否定的(虽然此地方可以运行,但是这句话还算对)

    A.fun2()  # 我是类方法,第一个参数是类
    A.fun3(2, 3)  # 我是静态方法,我的参数和此类毫无关系,不写参数也可以
    a.fun2()  # 我是类方法,第一个参数是类。(虽然传递的是对象,但是会知道该对象属于哪个类)
    a.fun3(1, 2)  # 我是静态方法,我的参数和此类毫无关系,不写参数也可以

在这里插入图片描述

三种方法除了使用上,其他地方有什么不同呢?

  • 静态方法类似普通函数,参数里面不用传递 self。有一些方法和类相关,但是又不需要类中的任何信息,出于对代码的理解和维护,就可用使用静态方法。静态方法最大的优点是能节省开销,因为它不会绑定到实例对象上,它在内存中只生成一个。
  • 而实例方法每个实例对象都是独立的,开销较大。
  • 而类方法与实例方法类似,但是类方法传递的不是实例,而是类本身。当需要和类交互而不需要和实例交互时,就可以选择类方法。
  • 48
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张烫麻辣亮。

谢谢老板支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值