Python之路day22-27-类面向对象

2,105次阅读
一条评论

前言时刻:学习了面向对象的基本概念,其实也没多少东西,主要是一些规范之类的

1、类

我们都知道 Python 是面向对象的一门语言,所以类是非常重要的一个概念,

  1. 每个定义的类都有一个空间,存储是定义在class中所有属性名称

  2. 每个实例化的对象都拥有自己的存储空间,通过对象名.__dict__,就可以获得这个对象的属性值。

  3. 对象的属性值有增删改查等方式,设有一个对象

    • 增:s.name="pyton"

    • 删除:del s.name

    • 查看:s.name或者s.__dict__['name']

  4. 不要将要计算的代码放到 __init__函数中执行。

2、类的基本概念

1)初始化类:

通过使用 __init__函数,初始化对象的属性。

# 1、初始化类

class A:

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

    def add(self):
        self.age = self.age + 1
        return self.age

a = A("python", 16)
a.add()   # 17

2)类的静态变量:

这个静态变量就有点佛系了,它不放在 __init__函数中初始化,而是和函数的位置一样,放在外面定义。可以通过类名访问或者对象名访问

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

    def add(self):
        self.age = self.age + 1
        return self.age

a = A("python", 16)

a.job   # data  通过对象访问
A.job   # data   通过类名访问

1)类的静态变量的用处:

  • 类被初始化后,其实例化的所有对象共享同一静态变量

2)静态变量的修改:

通过类.变量,修改变量值,如果是通过对象名.变量是修改对象的变量值。

class A:
    name = "Python"
    def __init__(self):
        self.name = "Java"
        self.job = 'data'
        print("初始化class A")
    def func(self):
        print('A func is called')

# 1、调用类的静态变量
a1 = A()
print(a1.name)  # Java
print(A.name)   # Python

# 修改类的静态变量
A.name = "C++"
print(A.name)   # C++

# 2、对象的属性添加变量
a1.ability = "99"   # 99
print(a1.ability)  # 99

# 3、类添加静态变量
A.is_active = True
print(A.is_active)   # True

3、命名空间:

当创建一个类的时候,会初始化一个类空间,里面存放着各个类属性的地址,如果类中有私有

  • Python中一切皆是对象,对象的类型即使类,函数也是类对象
  • 当使用对象名.属性的时候,会优先调用对象的属性,如果没有的话就找类的静态变量。

1)类的组合使用:

类的组合使用有利于解决类之间的绑定问题,将两个类绑定在一起,方便之间的调用。举个例子:今天老板让你写一个功能,在一个课程类中可以调用讲课的老师的姓名、年龄?

你会怎么办,这里可以使用类的组合功能。

class Teacher:

    def __init__(self, *args):
        self.name = args[0]

class Course:
    def __init__(self, *args):
        self.teacher = args[0]
        self.name = args[1]
    def get_teacher_name(self):
        return self.teacher.name

teacher1 = Teacher('zi', 18)

python_course = Course(teacher1, 'python')
python_course.get_teacher_name()   # zi

4、封装

其实就是Python的开放封闭原则,开放:指的是可以对该类进行扩展功能,而封闭:是可以控制一些属性不被外部访问

4.1 私有属性和方法:

具体的私有(private)用法是:

  • 双下划线+变量,是类的私有变量,只可以在本类中调用,且外部不能调用和修改。
  • 双下划线+方法,是类的私有方法,同样也是只可在本类中调用,外部不可调用。注意:其子类不可调用父类的私有变量和私有方法

公共(public)方法是:不加双下划线默认就是公共方法。

  • 父子均可调用,
  • 并且类外也可调用

使用私有方法,可以防止有些敏感数据被外部修改(其实也可修改,下面有提),提高安全性,复用性。

例子:

# 私有变量

class Person:

    def __init__(self, name, age, *args, **kwargs):
        self.name = name
        self.__age = age
        pass

    def __get_age(self):
        return self.__age + 600

    def get_msg2(self):
        return self.name, self.__get_age()

class Worker(Person):

    def __intit__(self, name, age, *args, **kwargs):
        self.w_age = age
        Super(Worker, self).__init__(name, age)

    def get_msg1(self):
        return self.get_msg2

1、私有属性 类外不可访问:

p = Person('jingdong', 18, 'python')
# p.age   # AttributeError: 'Person' object has no attribute 'age'
# p.get_age()   # AttributeError: 'Person' object has no attribute 'get_age'

# 想访问就需要通过类的成员函数 间接访问
p.get_msg2()   # ('jingdong', 618)

2、子类 也不可访问 父类的私有属性:

w = Worker('jingdong', 18, 'python')
# w.get_age()   # AttributeError: 'Worker' object has no attribute 'get_age'
w.get_msg2()   # ('jingdong', 618)

3、类外也可访问和修改私有属性:

我们打印下 p 对象的属性,发现原设置的__age变量变成了_Person__age

p.__dict__   # {'name': 'jingdong', '_Person__age': 18}

# 取类的私有变量
p._Person__age   # 18

# 修改类的私有变量
p._Person__age = 17
p._Person__age   # 17

4.2 property

这个方法挺好玩的的,它把一个函数看似变成了属性调用,比如self.func()变成了self.func,返回的结果一样。

使用 property 有啥优点呢?

  • 隐藏函数形式,
  • 缺点:不可以直接传参数,
# property装饰器

class Person:

    def __init__(self, name, age, *args, **kwargs):
        self.name = name
        self.age = age

    @property
    def get_name(self):
        return self.name

    @get_name.setter
    def get_name(self, te):
        if isinstance(te, str):
            self.name += te
        else:
            raise TpyeError(f"{te} must be str")

    def get_age(self):
        return self.age

p = Person('jingdong', 618, 'python')
print(p.get_name, p.get_age(), sep=' : ')   # jingdong : 618

# 可以看出,本来是函数,却用调用属性的方式进行调用。

修改属性:

p.get_name = '(666)'  
print(p.get_name)    # jingdong(666)

5、继承

继承主要为了解决代码冗余问题,子类可以调用父类的全部功能。在 Python3 中,所有的类都是默认继承于新式类 object 。

继承的称为子类或者派生类,被继承的称为父类或者父类。

继承可分为:

  • 单继承
  • 多继承:按照 C3 算法找父类的函数

另外还有新式类(Python3中的类都是新式类)和经典类(Python2中的)之分。

5.1 初始化父类

类中使用__init__函数进行类的属性构造,类似于 C++ 中的构造函数。

# 初始化父类

class A:
    def __init__(self, *args):
        self.age = args[0]
        self.name = 'java'
        pass

    def func(self):
        print('A', self.name, sep=' : ')

class B(A):
    def __init__(self, *args):
#         A.__init__(self, *args)    # 初始化父类 经典类的方法
        # super(B, self).__init__(*args)
        # super().__init__(*args)   # 新式类的初始化父类方法,和上一行等同。
        self.name = args[1]

    def func(self):
        print('B', self.name, sep=' : ')

B(17, 'python').func()  # B : python

5.3 调用同名函数/变量

class A:
    def __init__(self, *args):
        self.age = args[0]
        self.name = 'java'

    def func(self):
        print('A', self.name, sep=' : ')

class B(A):
    name = 'B name'
    def __init__(self, *args):
        super().__init__(*args)   # 初始化父类
        self.name = args[1]

    def func2(self):
        print('B', self.name, sep=' : ')

b_class = B(17, 'python')
b_class.func()   # A : python
b_class.name   # python
# 寻找name属性顺序:python->java->B name
  • 调用属性时,找属性的顺序:优先该类中->父类中->该类中的静态变量
  • 调用函数时:优先该类中->父类中

5.3 多继承

新式类中多继承的查找顺序规则是:MRO(module resolution order),其定义了调用父类属性的顺序问题,有点类似广度优先,但还是不一样。而在经典类中则采用深度优先的规则,来查找父类的属性。

多继承有两种方式,一种是使用super方法,另外一种是使用类名.__init__,那是一种系统的方法,

# 3、多继承

class Animal:
    def __init__(self, name, age, *args):
        print("初始化 Animal 类")
        self.age = age
        self.name2 = name

    def func1(self):
        print('Animal called', self.name2, sep=' : ')

class Single:
    def __init__(self, name, age, feature, *args):
        print("初始化 Single 类")
        self.feature = feature

    def func(self):
        print("Single called", self.name, self.feature)

class Dog(Single, Animal):
    name = 'Dog static'
    def __init__(self, name, age, feature, *args):
        Animal.__init__(self, '狗仔', 18)   # 初始化Animal类
        Single.__init__(self, name, age, feature)   # 初始化个体类
        self.name = name

    def func2(self):
        print('Dog', self.name, sep=' : ')

dog = Dog('旺财', 2, '柯基犬')
print(Dog.__mro__)   # (<class '__main__.Dog'>, <class '__main__.Single'>, <class '__main__.Animal'>, <class 'object'>)
dog.func1()
dog.name2    # 狗仔

"""
初始化 Animal 类
初始化 Single 类
(<class '__main__.Dog'>, <class '__main__.Single'>, <class '__main__.Animal'>, <class 'object'>)
Animal called : 狗仔
'狗仔'
"""

使用类名.__mro__打印出当前类的继承所有类的顺序。

2)使用super方法初始化父类:

这个需要重点掌握,因为很绕,

# 3、多继承

class Animal:
    def __init__(self, name, age, *args):
        print("初始化 Animal 类")
        self.age = age
        self.name2 = name

    def func1(self):
        print('Animal called', self.name2, sep=' : ')

class Single:
    def __init__(self, name, age, feature, *args):
        print("初始化 Single 类")
        self.feature = feature

    def func(self):
        print("Single called", self.name, self.feature)

class Dog(Single, Animal):
    name = 'Dog static'
    def __init__(self, name, age, feature, *args):
        super().__init__(name, age, feature)   # 初始化Single类
        super(Single, self).__init__('狗仔', 18, feature)   # 初始化Animal类
        self.name = name

    def func2(self):
        print('Dog', self.name, sep=' : ')

dog = Dog('旺财', 2, '柯基犬')
print(Dog.__mro__)
dog.func1()
dog.name2   # 狗仔

"""
初始化 Single 类
初始化 Animal 类
(<class '__main__.Dog'>, <class '__main__.Single'>, <class '__main__.Animal'>, <class 'object'>)
Animal called : 狗仔
'狗仔'
"""

3)mor算法介绍:

6、多态

其实前面已经用到了多态的概念,就是在继承中。在c++中,多态指的是:通过传入不同的参数类型和数量实现不同的功能。在 Python 中也是类似的,子类继承于父类,在子类中写一个和父类的同名函数。调用函数的时候,就可有不同用法。

多态有啥优点:

  • 不破坏父类的结构,可添加自定义功能
  • 可定义同名函数,覆盖父类的函数功能
class Person:

    def __intit__(self):
        pass
    def func(self, name):
        print(f"Person {name}")

    def get_name(self):
        print("func get_name")

class Boy(Person):

    def __init__(self):
        super().__init__()
        pass
    def func(self, name):
        print(f"Boy {name}")

xm = Boy()

# 多态
xm.func('小明')   # Boy 小明

# 继承
xm.get_name()   # func get_name

6.1 多态的用处

多态可以用来做项目架构,事先定义一个父类,等到后面只需要定义一些子类,重写一些函数即可,高效而简洁。

# 多态的用处

import abc

class Person(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def get_name(self):
        pass

class Boy(Person):

    def get_name2(self):
        print('Boy')

xm = Boy()
xm.get_name()
# TypeError: Can't instantiate abstract class Boy with abstract methods get_name

如果子类不定义一个同名函数,直接就报错。

7、classmethod用法

从字面上的意思就是:类方法,官方的解释就是在不实例化对象的时候,可以通过类名或者对象调用被装饰的函数。用法很简单直接给函数添加一个装饰器:@classmethod,被装饰器的函数的第一个参数必须是cls

class A(object):
    __age = 17   # 类静态变量
    __age2 = 17

    @classmethod
    def get_age(cls, age):
        cls.__age += age
        return cls.__age

    def get_age2(self, age):
        self.__age += age
        return self.__age

# 直接就可通过类名访问类方法
A.get_age(1)   # 18

# 通过实例化的对象调用
a = A()
a.get_age2(1)   # 19

2)思考🤔:这个classmethod有啥用?

个人觉的最大的好处就是不用实例化类就可调用成员函数,并且默认给函数传进取了一个cls,其实就是 self,可以初始化一个对象并返回,可以看下面:

class A(object):

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

    @classmethod
    def get_obj(cls, age):
        return cls(age)

# 直接就可通过
a = A.get_obj(666)
print(a)   # <__main__.A object at 0x7f8f1ab4b670>
a.age   # 666

8、staticmethod方法

他是放在类中的一个普通函数,不带有self参数,想当时与专属于这个类的一个局部函数。

# staticmethod方法

class A(object):
    @staticmethod
    def get_age(age):
        print("我就是一个普通的函数,只不过在类中")
        return age

# 1. 通过类名访问
A.get_age(1)   # 1

# 2. 通过对象名访问
a = A()
a.get_age(2)   # 2

总结

面向对象的东西很多很杂,还是那句话,多看多练多总结!

参考链接:

http://www.langzi.fun/Python%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B.html

多继承:

https://blog.csdn.net/Jack_Zhao_/article/details/82840901

https://blog.csdn.net/RSstudent/article/details/108809921

https://www.cnblogs.com/wongbingming/p/10934356.html

2
西园公子
版权声明:本站原创文章,由西园公子2021-06-27发表,共计8487字。
转载提示:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(一条评论)
载入中...
西园公子 博主
2021-07-06 17:12:00 回复

我自己评论一下 :razz: