Python面向对象以及封装、继承、多态初识

python面向对象

面向对象与面向过程,不多说

类和对象

类和对象是面向编程的两个核心概念,java中亦是如此

总而言之,类是模板,对象是实例

类的模板

class Human:
    """
    此类主要是构建人类
    """
    mind = '有思想'  # 第一部分:静态属性 属性 静态变量 静态字段
    dic = {}
    l1 = []
    def work(self): # 第二部分:方法 函数 动态属性
        print('人类会工作')
# class 是关键字与def用法相同,定义一个类。
# Human是此类的类名,类名使用驼峰(CamelCase)命名风格,首字母大写,私有类可用一个下划线开头。
# 类的结构从大方向来说就分为两部分:

静态变量。
动态方法。

面向对象基本语法

定义类:使用class来定义一个类,类名一般需要遵守大驼峰命名法

  • class 类名:
  • class 类名(object):
class Student(object):  # 关注这个类有哪些属性和行为
     # 这个属性直接定义在类里,是一个元组,用来规定对象可以存在的属性
    __slots__ = ('name', 'age', 'height')
    
    
    # 在__init__方法里,以参数的形式定义属性,我们称之为属性
    def __init__(self, name, age, height):
        self.name = name
        self.age = age
        self.height = height

    # 还可定义一些方法
    def run(self):
        print('正在跑步')

    def eat(self):
        print('正在吃东西')


# 通过Student类创建对象
s1 = Student('小明', 20, 175) # 自动调用__init__方法
s1.run()
print(s1.hobby) # 注意:如果没有这个属性的话会报错

# 直接使用等号给一个属性赋值(这点与java不同,java是定死了的)
# 如果这个属性以前不存在,会给对象重新添加一个新的属性
# 如果这个属性以前存在的话,那么就会修改这个属性的值
s1.sleep = True
print(s1.sleep)


魔法方法1

也叫魔术方法,是类里面的特殊方法,不需要手动调用

特点:

  • 不需要手动调用,会在合适的时机自动调用
  • 这些方法,都是使用下划线__开始,使用下划线结束
  • 方法名都是系统规定好的,在合适的时机自己调用
import time


class Person(object):
    # 在创建对象时,会自动调用这个方法
    def __init__(self, name, age):
        print('__inti__方法被调用了')
        self.name = name
        self.age = age

    # 当对象被销毁时,会自动调用这个方法
    def __del__(self):
        print('__del__方法被调用了')

    def __str__(self):
        return '姓名:{},年龄:{}'.format(self.name, self.age)

    def __repr__(self):
        return 'world'

    def __call__(self, *args, **kwargs):
        print('__call__方法被调用')
        # args 是一个元组,保存(1,2)
        fn = kwargs['fn']
        # return fn(args[0], args[1])
        return kwargs['fn'](args[0], args[1])


p1 = Person('小皮', 18)
# 如果不做任何修改,直接打印一个对象,是对象的类型以及内存地址
# 直接打印对象的话,会调用这个对象的__str__或者__repr__方法
# 如果上述的两个方法都重写了,会选择__str__方法
print(p1)
# time.sleep(10)

print(repr(p1))  # 调用内置函数repr会触发对象的__repr__方法
print(p1.__repr__())  # 也可以手动调用魔法方法
result = p1(1, 2, fn=lambda x, y: x + y)  # 调用对象的__call__方法
print(result)

总结

当创建一个对象时,会自动调用__init__方法,当删除一个对象时,会自动调用__del__方法

魔法方法2

import time


class Person(object):
    # 在创建对象时,会自动调用这个方法
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        print(f'__equ__方法被调用了,other ={other}')
        if self.name == other.name and self.age == other.age:
            return True
        else:
            return False
	

p1 = Person('小明', 18)
p2 = Person('小明', 18)

# p1和p2是同一个对象嘛
# 怎么比较两个对象是同一个对象呢?比较的是内存地址
print('ox%x' % id(p1))  # ox1df8370
print('ox%x' % id(p2))  # ox1e0e3a0

# is 身份运算符,可以从来判断两个人对象是否是同一个对象
print(p1 is p2)  # False
print('p1 == p2', p1 == p2)  # False

# 其实 == 会调用对象的__eq__方法,获取返回值,如果__eq__方法不重写,仍然比较的是内存地址

运算符相关的魔法方法
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

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

    def __ne__(self, other):  # 使用 != 运算符会使用这个方法
        return '不等运算符'

    def __gt__(self, other):  # 使用 > 运算符会调用(如果没有重写这个方法,那么就会报错)
        return '大于运算符'

    def __ge__(self, other):  # 使用 >= 运算符会调用(如果没有重写这个方法,那么就会报错)
        return '大于等于运算符'

    def __lt__(self, other):  # 使用 < 运算符会调用(如果没有重写这个方法,那么就会报错)
        return '小于运算符'

    def __le__(self, other):  # 使用 <= 运算符会调用(如果没有重写这个方法,那么就会报错)
        return '小于等于运算符'

    def __add__(self, other):  # 使用 + 运算符会调用
        return '加法运算符'

    def __sub__(self, other):  # 使用 - 运算符会调用
        return '减法运算符'

    def __mul__(self, other):
        return '乘法运算符'

    def __truediv__(self, other):
        return '除法运算符'

    def __str__(self):
        return f'name:{self.name},age:{self.age}'

    def __int__(self):
        return self.age


p1 = Person("张三", 18)
p2 = Person("张三", 18)

print(p1 is p2)  # False 判断两个是不是同一个对象
print(p1 == p2)  # True 会调用__equ__方法

print(p1 != p2)  # hello,会调用 __ne__方法或者__eq__方法取反
print(p1 > p2)
print(p1 >= p2)
print(p1 < p2)
print(p1 + p2)
print(p1 - p2)

# str() 将对象转换为字符串,会自动调用__str__方法,打印也会调用
print(str(p1))  # 转换为字符串 <__main__.Person object at 0x0000000000688400>
print(int(p1))  # 会调用__int__方法

内置属性

python类中自带的一些属性

class Person:
    """
    这是类的文档注释
    """

    # __slots__ = ('name', 'age')  # 规定有哪些属性

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

    def eat(self):
        print(self.name + ' 正在吃饭')


p = Person('张三', 18)
# print(dir(p))  # 查看对象的所有属性、行为

"""
内置属性
"""

# __class__
print(p.__class__)  # <class '__main__.Person'>
# __dict__ 把对象的属性和值转成字典,等价于 dir(p)
print(p.__dict__)  # {'name': '张三', 'age': 18}
# __doc__
print(p.__doc__)  # 查看当前对象的文档,也可以通过Person.__doc__ 类名获取
# __module__
print(p.__module__)  # 获取模块名 __main__
# __slots__
# print(p.__slots_)

把对象当做字典使用

dict的使用,类似java中的getter、setter方法

class Person:

    def __init__(self, name, age, city):
        self.name = name
        self.age = age
        self.city = city

    def __setitem__(self, key, value):
        print(f'__setitem__方法被调用了,key={key},value={value}')
        self.__dict__[key] = value

    def __getitem__(self, item):
        return self.__dict__[item]


p = Person('张三', 18, '深圳')
print(p.__dict__)  # 将对象转换成字典

# 不能直接把一个对象当做字典来使用
p['name'] = 'pihao'  # []语法 会调用对象的__setitem__方法,所有要重写这个方法才能使用
print(p.name)

p.__dict__['name'] = 'haoming'
print(p.name)

# 默认是不能直接使用这种方法来获取属性值的,需要重写__getitem__方法
print(p['name'])

类属性和对象属性

类似java中的实例方法以及类方法(静态方法)

  • 对象属性:每个实例对象都单独保存的属性
  • 类属性: 保存在类中,所有的实例对象共有,只能通过类对象来修改值,不能通过实例对象来修改
class Person:
    type = '人类'  # 类属性

    def __init__(self, name, age):
        self.name = name # 对象属性
        self.age = age


p1 = Person('张三', 19)
p2 = Person('李四', 20)

p1.type = 'human'  # 注意:实例对象并不会修改类属性,而是给实例对象添加了一个的对象属性
Person.type = 'monkey'  # 这样才是修改了

print(p1.type)
print(p2.type)
print(Person.type)

私有属性和私有方法

类似java中被private修饰的变量,只能在类中调用或者定义get|set方法

class Person:

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.__money = 1000  # 以两个下划线开头的变量是私有变量,有点类似java中private修饰的变量

    def test(self):
        self.__money += 10  # 在类中可以访问私有属性

    def get_money(self):
        return self.__money

    def set_money(self, money):
        self.__money = money

    def __demo(self):
        print('我是__demo函数')  # 以两个下划线开始的函数,是私有函数,在外部无法调用


p = Person('张三', 18)
print(p.name)
# print(p.__money)  # 拿不到__money的值

# 获取私有变量的方式
# 1、使用对象._类名__私有变量名获取
print(p._Person__money)  # 1000,没有绝对的私有
p.test()
print(p._Person__money)  # 1010
# 2、定义get和set方法来获取
print(p.get_money())
p.set_money(100)
print(p.get_money())
# 3、使用property来获取(知道)

# p.__demo()  # 不能调用,只能在类的内部调用
p._Person__demo()  # 或者这样调用

类方法以及静态方法

类似java中被static修饰的方法,

  • 静态方法:没有使用到实例对象的任何属性,那么你就可以声明为静态方法,常在工具类中使用
  • 类方法:如果这个函数只用到了类属性,我们可以定义为一个类方法
import time


class Person(object):
    type = 'human'  # 定义一个类属性

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

    # 实例方法
    def eat(self, food):
        print(f'{self.name}正在吃{food}')

    # 如果一个方法里没有用到实例对象的任何属性,可以将这个方法声明为static静态方法
    @staticmethod
    def help():
        print("使用说明.....")

    # 如果一个方法只使用到了类属性,那么可以定义为一个类方法
    @classmethod
    def test(cls):  # 这里的cls其实是Person类对象
        print(cls.type)


# 实例对象调用
p = Person('小明', 18)
p.eat('披萨')  # 小明正在吃披萨

Person.eat(p, '火锅')  # 如果使用类对象调用的方法的话,需要手动指定实例self

# 静态方法调用
Person.help()  # 静态方法调用,没有用到实例对象的任何属性
p.help()

# 类方法调用(可以使用类或者实例调用)
Person.test()
p.test()

继承

面向对象的三大特点:封装、继承、多态

  • 封装:函数是对语句的封装,类是对函数和变量的封装
  • 继承:类和类之间可以认为手动的建立父子关系,父类的属性和方法,子类可以使用
  • 多态:是一种技巧,提高代码的灵活度
# 继承简单使用

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

    def sleep(self):
        print(f'{self.name}正在睡觉')


class B(A):  # 继承A类
    def bark(self):
        print(f'{self.name}正在叫')


a = B('大黄', 3)

# a = B()  会报错,首先它会调用__new__方法,再调用__init方法
a.bark()

python继承的注意事项

  • python允许多继承,这点和java区分开,java只能单继承
  • 如过不同的父类中有同名的方法,在调用的时候就是就近原则(就是在继承的括号里前面的优先),与继承的顺序有关
  • 继承的时候是深度优先还是广度优先?(深度优先,了解),查看类的__mro__属性。就是继承的调用顺序

面向对象的相关方法

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


class Student(Person):
    pass


p1 = Person('张三', 18)
p2 = Person('李四', 20)
print(p1 is p2)  # is 运算符是用来判断两个对象是否是同一个对象,比较的是内存地址  id(p1) == id(p2)

if type(p1) == Person:  # type(p1) 其实获取的就是类的对象
    print('p1是Person类创建的对象')

# isinstance 用来判断一个对象是否是由指定的类(或者父类)实例化出来的
s = Student('Jack', 20)
print(isinstance(s, Student))  # True
print(isinstance(s, Person))  # True
# issubclass 用来判断一个类是否是另一个类的子类
print(issubclass(Student, Person))  # True
print(issubclass(Person, Student))  # False

多态

子类重写父类的方法

继承特点:如果一个类A继承自类B,那么由A创建出来的实例对象都能直接使用类B定义的方法

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

    def sleep(self):
        print(f'{self.name}正在睡觉')


class Student(Person):
    def __init__(self, name, age, school):
        # 使用super直接调用父类的方法,推荐这种方式
        super(Student, self).__init__(name, age)
        self.school = school

    def sleep(self):
        print(f'{self.name}正在课间休息时睡觉')


s = Student('Jack', 20, 'xxx大学')
s.sleep()

多态的使用

多态是基于继承的,通过子类重写父类的方法,达到不同的子类对象调用相同的父类方法,得到不同的结果,提高代码的灵活度

不使用多态的问题

代码十分冗余

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w0QzY5QT-1599904182378)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200912161644650.png)]

使用多态优化代码
class Dog:
    def work(self):
        print('狗狗正在工作')


class PoliceDog:
    def work(self):
        print('警犬正在攻击敌人')


class BlindDog:
    def work(self):
        print('导盲犬正在领路')


class DrugDog:
    def work(self):
        print('缉毒犬正在搜毒')


class Person:
    def __init__(self, name):
        self.name = name
        self.dog = None

    def work_with_dog(self):
        if self.dog is not None and isinstance(self.dog, Dog):
            self.dog.work()


p = Person('张三')

pd = PoliceDog()
p.dog = pd
p.work_with_dog()

pd = BlindDog()
p.dog = pd
p.work_with_dog()

pd = DrugDog()
p.dog = pd
p.work_with_dog()

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值