Python基础 2.3 面向对象2

2.3 面向对象2 私有权限,继承,多态

2.3.1 私有权限

1.面向对象的特性

  1. 面向对象三大特性:封装,继承,多态(函数由于参数不同实现不同效果)
  2. 面对对象的封装特性:将属性和方法封装成一个整体,通过实例化来处理。对类的属性和方法增加权限控制。

python下一切皆对象

1.变量是对象 a = 10
2.容器也是对象 list_my = [1,2,3,4]
3.函数也是对象 
def func():
	
print(func) ==> class function

python里函数都为多态

2.私有属性

​ 如果在属性名前面加了2个下划线__,则表明该属性为私有属性,否则为公有属性。

私有属性只能在类的内部访问。外部创建的实例无法访问。

class Dog(object):
    def __init__(self):
        self.__baby_count =0
        self.age = 1

    def print_info(self):
        print(self.__baby_count)


dog1 = Dog()
print(dog1.age)
dog1.print_info()
print(dog1.__baby_count)
输出
1
0
Traceback (most recent call last):
  File "E:/Project/pythonProject/002.py", line 697, in <module>
    print(dog1.__baby_count)
AttributeError: 'Dog' object has no attribute '__baby_count'

可以看出,我们创建的实例dog1不能直接访问自己的隐藏属性,用print打印不出来。

但是可以通过类内部定义的方法来访问。

3.私有方法

​ 在方法名的前面加2个下划线__,一样,只能在类的内部使用,不能在外部使用。一样需要用self来访问。

class Dog(object):
    def __init__(self):
        self.__baby_count =0
        self.age = 1

    def print_info(self):
        print(self.__baby_count)
        self.__bite_dog()
        
    def __bite_dog(self):
        print("不许咬人")


dog1 = Dog()
print(dog1.age)
dog1.print_info()
dog1.__bite_dog()
输出
Traceback (most recent call last):
  File "E:/Project/pythonProject/002.py", line 700, in <module>
    dog1.__bite_dog()
AttributeError: 'Dog' object has no attribute '__bite_dog'
1
0
不许咬人

可以看出我们没法用实例直接调用私有方法__bite_dog但是我们可以用可以访问的类的方法,通过类的内部访问。

2.3.2 继承

1.继承是什么

​ 继承描述的是类与类的关系,父类派生出子类,子类继承父类。父类也叫基类,子类也叫派生类。

2.继承的作用

​ 子类直接具备父类的能力,包括属性和能力,解决代码重用问题。

3.代码实例

class Animal(object):
    def __init__(self, _type, _name):
        self.type = _type
        self.name = _name
        self.__goss = '这是父类的隐藏属性'

    def __str__(self):
        return f"这是一种名为{self.name}的{self.type}类型的动物"

    def __goff(self):
        print("这是父类的隐藏方法")

    def print_info(self):
        # print(self.__goss)
        self.__goff()


class Dog(Animal):
    def __init__(self, _type, _name):
        self.type = _type
        self.name = _name

    def __str__(self):
        return f"这是一只叫{self.name}的{self.type}的狗"

    def print_finfo(self):
        # print(self.__goss)
        self.__goff()



dog1 = Dog('金毛', '小黑')
print(dog1)
# dog1.print_info() # 查看子类的实例是否能继承了父类的隐藏属性和隐藏方法
dog1.print_finfo() #查看是否子类能直接调用父类的隐藏属性和方法
print(Dog.__base__) # 查看子类的父类名
print(dir(dog1)) # 查看该类的所有隐藏方法

ad = Animal("熊", "大熊")
ad.print_info()

可以看出son1是Son对象,却实际上可以使用父类的属性和对象。拓展,有个内置的魔法属性可以查看类的父类。类名.__bases__ 可以查到这个类的父类。

子类对象调用方法有一个就近原则

  • 如果本类能找到方法,直接调用本类的方法
  • 如果本类找不到,则调用父类继承过来的方法

4.单继承和多层继承

1.单继承

​ 子类只继承一个父类

2.多层继承
继承关系为多层传递
3.重写父类方法
1.子类重写父类同名方法

​ 父类的方法不能满足需求,重写父类的方法目的是为了拓展功能

在子类中定义和一个父类同名方法,即为对父类方法重写,子类调用同名方法默认是子类的

class Animal(object):
    def __init__(self, _type, _name):
        self.type = _type
        self.name = _name

    def __str__(self):
        return f"这是一种名为{self.name}的{self.type}类型的动物"


class Dog(Animal):
    def __init__(self, _type, _name):
        self.type = _type
        self.name = _name

    def __str__(self):
        return f"这是一只叫{self.name}的{self.type}的狗"


dog1 = Dog('金毛', '小黑')
print(dog1)
2.子类调用父类同名方法
    def print_info(self, _type, _name):
        print(f"这是{self.type}的狗")
        print("="*20)
# 方法1   父类名.同名方法(self,形参1 ····)
#         Animal.__init__(self, _type, _name)
          Animal.print_info(self)
# 方法2   super(子类名,self).同名方法(参数1····) 这个方法调用的是该类名下一级的顺序找,按__mro__得到的顺序列表
        super(Dog, self).print_info()
# 方法3:super().同名方法(形参1, ……) # 是 4.2 方法的简写
# 推荐使用的方法
#       super().__init__(_)
        super().print_info()
输出
这是一只叫小黑的金毛的狗
这是金毛的狗
====================
这是金毛的动物
这是金毛的动物
这是金毛的动物

5.多继承

1.多继承的定义和格式

​ 一个子类有多个父类,并且具有他们的特征。

class BigDog(object):
    def eat(self):
        print("大口吃肉")


class SmallDog(object):
    def drink(self):
        print("小口喝水")


class SuperDog(BigDog,SmallDog):
    def eat(self):
        print("吃罐头")
        print("="*30)
        BigDog.eat(self)
        print("="*30)
        super().eat() 


sd = SuperDog()
sd.drink()
sd.eat()
输出如下
小口喝水
吃罐头
==============================
大口吃肉
==============================
大口吃肉
2.多继承的继承顺序

​ 查看类的继承顺序 类名.__mro__是一种默认的魔法属性,输出的就是实例里寻找方法属性的优先顺序。

print(SuperDog.__mro__)
输出
(<class '__main__.SuperDog'>, <class '__main__.BigDog'>, <class '__main__.SmallDog'>, <class 'object'>)

如果多个父类顺序有相同方法,而且你想调用最上级的而不是自己上一级的。用那种父类名.方法名的就行。

super()函数不能套套。

6.私有和继承

​ 父类的私有方法和私有属性,子类的实例也不能直接继承使用,也是需要在父类的内部使用。不能使用子类的方法去读取。得使用父类的方法,才能使用父类的隐藏方法和隐藏属性

**通过dir()可以看出查看也就是方法继承了,属性没有继承。**这是因为没有调用父类的__init__()方法,并没有定义。

class Animal(object):
    def __init__(self, _type, _name):
        self.type = _type
        self.name = _name
        self.__goss = '这是父类的隐藏属性'

    def __str__(self):
        return f"这是一种名为{self.name}的{self.type}类型的动物"

    def __goff(self):
        print("这是父类的隐藏方法")

    def print_info(self):
        # print(self.__goss)
        self.__goff()


class Dog(Animal):
    def __init__(self, _type, _name):
        self.type = _type
        self.name = _name

    def __str__(self):
        return f"这是一只叫{self.name}的{self.type}的狗"

    def print_finfo(self):
        # print(self.__goss)
        self.__goff()



dog1 = Dog('金毛', '小黑')
print(dog1)
dog1.print_info() # 查看子类的实例是否能通过父类去使用隐藏方法 
# dog1.print_finfo() #查看是否子类的实例能否通过子类去使用隐藏方法(不行,报错)
print(Dog.__base__) # 查看子类的父类名
print(dir(dog1)) # 查看该类的所有隐藏方法

7.拓展

​ 实际上python里的私有权限私有属性是给更改名字了。

我们通过更改名,就可以直接访问。

格式

print(对象._类名隐藏属性名)
对象._类名隐藏方法名()

这样的话对应类的实例都是可以直接访问的,但是一样的,子类无法通过此访问父类的隐藏属性,因为没有继承。

2.3.3 多态

​ 调用同一函数,不同表现。

因为python本身是动态语言,本身就是多态。

实现多态的补轴

  1. 实现继承关系
  2. 子类重写父类方法
  3. 通过对象调用该方法
class Animal(object):
    def eat(self):
        print("吃东西")


class Dog(Animal):
    def eat(self):
        print("啃骨头")


class Cat(Animal):
    def eat(self):
        print("吃小鱼")


def func(temp):
    temp.eat()


cat1 = Cat()
dog1 = Dog()
func(cat1)
func(dog1)
输出
吃小鱼
啃骨头

可以看出,同一个函数,因为传递进去的实例不同,因为都有重写父类方法,所实现的方法也不同。python的多态是常态。

2.3.4 实例属性,类属性

1.实例属性和类属性

1.1 专业名词说明
  • 在python中,万物皆对象
  • 通过类创建的对象,又称为实例对象对象属性又称为实例属性
  • 类本身也是一个对象,执行class语句的时候被创建出来的,我们称之为类对象,为了区分实例对象,我们也简称为类。
1.2 实例属性
  • 通过在__init__()方法里给实例添加的属性。
  • 类的外面直接通过实例对象添加 的属性。
  • 实例属性必须通过实例对象才能访问
# 定义类
class 类名(object):
    def __init__(self):
        self.实例属性变量1 = 数值1
        self.实例属性变量2 = 数值3

# 创建实例对象
实例对象名 = 类名()

# 添加属性
实例对象名.实例属性变量3 = 数值3
1.3 类属性
  • 类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象所拥有
  • 定义在类里面类方法外面的变量就是类属性
  • 类属性可以用类名实例对象访问,推荐使用类名访问。
class Dog(object):
    # 类属性
    count = 0

    def __init__(self):
        # 实例属性
        self.name = '大黄狗'


# 类名访问类属性 推荐使用这种方法
print(Dog.count)
# 创建实例对象
dog1 = Dog()
# 实例对象访问类属性
print(dog1.count)
# 实例对象访问实例属性
print(dog1.name)
1.4 类属性和实例属性区别
  • 类属性就是 类对象 所拥有的属性,他被该类所有的实例对象共有
  • 实例属性要求每个对象为其开辟一份独立内存空间,只属于某个实例对象
# 定义一个count的类属性来计算实例对象初始化的次数
class Dog(object):
    # 类属性
    count = 0

    def __init__(self):
        # 实例属性
        self.name = '大黄狗'
        Dog.count += 1


# 类名访问类属性
print(Dog.count)
# 创建实例对象
dog1 = Dog()
dog2 = Dog()
dog3 = Dog()
dog4 = Dog()
dog5 = Dog()
dog6 = Dog()
print(Dog.count)
print(dog1.count,dog2.count·····)
1.5 类属性更改
1)类属性只能通过类对象访问更改,不能通过实例对象更改

如果对实例对象企图对类属性更改,系统会默认操作给你赋值一个新的实例属性,而非更改了类属性。

class Dog(object):
    # 类属性
    count = 0

    def __init__(self):
        # 实例属性
        self.name = '大黄狗'
        Dog.count += 1


# 类名访问类属性
print(Dog.count)
# 创建实例对象
dog1 = Dog()
dog2 = Dog()
print(Dog.count)
Dog.count = 1
print(Dog.count)
dog1.count = 250
print(Dog.count, dog1.count)

输出

0
2
1
1 250

可以看出实例对象企图更改导致创建了一个实例属性

这导致了另外一个问题

2)如果实例属性和类属性名一致,那么实例对象只能访问实例属性

此时实例对象无法访问类属性

3)类属性也能更改为私有类属性,加2个下划线即可

私有类属性也一样不能在类的外部使用访问,但是可以在内部使用。

2. 类方法,静态方法,实例方法

1.1类方法
  • 类对象所拥有的方法,主要在没有创建实例对象的前提下,处理类属性
  • 需要用装饰器 @classmethod来标志其为类方法
  • 对于类方法,第一个参数必须是类对象代表类),一般以cls作为第一个参数,这个参数不需要手动传参,处理器会自动传。
  • 类方法的调用
    • 类名.类方法() 推荐用法
    • 实例对象.类方法()
class Dog(object):
    count = 0

    @classmethod
    def print_count(cls):
        print(type(cls),cls) 
        print("cls.count = ", cls.count)


Dog.print_count()
输出
cls.count =  0

dog1 = Dog()
dog1.print_count()
输出
<class '__main__.Dog'> <class 'type'>
cls.count =  0
<class '__main__.Dog'> <class 'type'>
cls.count =  0
# 可以看出即使是使用实例对象调用类方法,实际上传参传进去的依旧是类对象,而非实例对象。
1.2 静态方法
  • 需要通过装饰器@staticmethod来进行修饰,静态方法默认情况下, 既不传递类对象也不传递实例对象(形参没有self/cls)
  • 当方法中 既不需要使用实例对象也不需要使用类对象时,定义静态方法
  • 作用:取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
  • 静态方法 也能够通过 实例对象类对象(类名) 去访问。
class Dog(object):
    count = 0

    @classmethod
    def print_count(cls):
        print(cls, type(cls))
        print("cls.count = ", cls.count)

    @staticmethod
    def print_static():
        print("这是一个不需要传递任何参数的静态方法")


dog1 = Dog()
Dog.print_static()
dog1.print_static()
1.3 实例方法与类方法,静态方法的区别总结
  • 定义区别
class Dog(object):
    def print1(self): # 实例方法
        pass
    
    @classmethod # 类方法
    def print2(cls):
        pass
    @staticmethod # 静态方法
    def print3():
        pass
  • 调用方法区别

    • 实例方法必须通过实例对象调用
    • 类方法,实例方法均可通过类对象,实例对象调用,但是推荐类对象调用
  • 定义原则

    • 当方法中需要实例属性,我们定义为实例方法
    • 当方法中只需要使用类属性,定义为类方法
    • 当不需要类属性和实例属性,使用静态方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值