前言时刻:学习了面向对象的基本概念,其实也没多少东西,主要是一些规范之类的
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面向对象编程.html
多继承:
https://blog.csdn.net/JackZhao/article/details/82840901