前言时刻:学习了面向对象的基本概念,其实也没多少东西,主要是一些规范之类的
1、类
我们都知道 Python 是面向对象的一门语言,所以类是非常重要的一个概念,
-
每个定义的类都有一个空间,存储是定义在class中所有属性名称
-
每个实例化的对象都拥有自己的存储空间,通过
对象名.__dict__
,就可以获得这个对象的属性值。 -
对象的属性值有增删改查等方式,设有一个对象
-
增:
s.name="pyton"
-
删除:
del s.name
-
查看:s.name或者
s.__dict__['name']
-
-
不要将要计算的代码放到
__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),其定义了调用父类属性的顺序问题,有点类似广度优先,但还是不一样。而在经典类中则采用深度优先的规则,来查找父类的属性。
多继承有两种方式,一种是使用supe
r方法,另外一种是使用类名.__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
我自己评论一下