OOP
类与对象是面向对象编程的两个主要方面。
一个类(Class)能够创建一种新的类型(Type)
其中对象(Object)就是类的实例(Instance)
规范一些称呼
- 类或对象中的变量称作字段
- 字段有两种
- 实例变量(对象私有)
- 类变量(所有对象共享一份 可以通过类来调用)
- 字段有两种
- 类中的函数称作方法
- 字段和方法都是所属类的属性
定义类
定义一个类的格式
# 类名的首字母要大写
class 类名(object): # 最顶层的类写(object) 也可以不写
def method1(self): # 这个self必须写
pass
----------------------------------------------------------
class A: # 定义一个类
pass
a = A() # 实例化一个类的对象
print(A) # <class '__main__.A'>
print(a) # <__main__.A object at 0x000002B312C6D128>
类中可以定义若干个变量,以及定义类中的方法
注
在python2中有两种类 经典类/新式类 py3中的统一都是新式类
py2中没有继承object的话 该类就是一个经典类
要声明新式类需要继承object
对象
python中实例化对象
# python是不需要new的 直接类()
变量 = 类(参数)
# 变量接收到一个新创建的类的实例
# 然后通过变量通过.来调用对象中的实例属性(实例方法和实例变量)
在类中,普通方法的参数列表中都要有一个self,这个self表示对象本身
如果想调用其他实例属性的话,需要在标识符前面加上self.
class A(object):
a = 1
b = 2
def __init__(self, a=1, b=2):
self.a = a
self.b = b
def ab(self):
print(self.a + self.b)
def printab(self):
self.ab()
ab = A()
ab.printab()
个人看法:方法是从设计层面
函数是程序运行,过程式的一种称为
可以大概理解为 类是一种生产饼干的配方,而制作出来的饼干是对象
有了配方可以产生很多饼干
加入不同的调料可以产生不同类型的饼干
通过传入不同的值来产生不同的对象,而且每实例化一次类就会生成一个对象
ab3 = A()
ab1 = A()
ab2 = A()
print(hex(id(ab1)))
print(hex(id(ab2)))
print(hex(id(ab3)))
# 输出结果
0x257f3713400
0x257f370b0f0
0x257f3514a90
字段
就是类中的变量
类变量的增删改查
class A:
method = 20 # 在其他语言中 就是带static的静态变量
print(A.method1) #查看一个类属性
A.value1 = 100 #给一个类属性重新赋值
del A.value #删除类属性
A.value2 = 200 #增加新的类属性
也可以通过类.__dict__
来进行修改
实例变量和类变量的操作方式几乎一样
在类中定义为self.xxx = value的就是实力变量
这个self其实可以换成其他名
方法
init
__init__
构造方法
作用:初始化对象的成员属性
以前用java初始化成员属性的时候
int a;
类(int a){
this.a = a;
}
# 而静态变量就是可以直接通过类来调用的"类变量"
static b = 2;
在python中
b = 2 # 类变量 可以直接通过类来访问
def __init__(self,a):
self.a=a # 实例属性
实例化对象的时候,在类()传参,就是在给__init__()
进行传参
还有__init__()
必须返回None 也就是不写return
类中 定义方法 和在类的外面定义函数是一样的,不过类中的方法需要写一个默认的self参数作为对象本身
@property
class A:
@property
def a123(self):
print(123)
a = A()
a.a123 # 被@property修饰的方法可以被当做变量来调用
类方法与静态方法
类方法只需要通过类来调用方法
# 格式
# 类方法
@classmethod
def A(cls):
pass
# 静态方法
@staticmethod
def B()
pass
类方法要加上装饰器@classmethod
类方法的()里要传一个参数cls 这个形参接收的是一个类名
@classmethod
def c(cls):
print(123)
类方法就是用来操作类属性的,和对象无关的
尽量不要通过对象来调用类方法
还有静态方法
@staticmethod
def d():
print(456)
调用的话直接通过类调用
python的类中可以有三种方法
- 普通方法(self,…) 和对象绑定 不需要装饰器 由对象调用 可以使用成员属性 类名.类属性
- 类方法(cls,…) 和类绑定 @classmethod 由类调用 不能使用成员属性 cls.类属性
- 静态方法(…) 类的工具包 @staticmethod 由类调用 不能使用成员属性 类名.类属性
class A:
a123 = 1
@classmethod
def a123(cls):
print(cls.a123)
@staticmethod
def a123_n():
print(A.a123)
def a123_n2(self):
print(A.a123)
类的组合
类和类之间的关联
class gn_a:
def m1(self):
print(123)
class gn_b:
def m1(self):
print(456)
class gn_c:
def m1(self):
print(678)
class gn:
def __init__(self, a, b, c):
# 将其他类的实例化对象赋值给主类的实例变量
# 然后在类中可以通过实例变量来调用相应对象中的属性
self.gn_a = a()
self.gn_b = b()
self.gn_c = c()
def m1(self):
self.gn_a.m1()
self.gn_b.m1()
self.gn_c.m1()
gn = gn(gn_a, gn_b, gn_c)
gn.m1()
OOP特性
继承
A类继承了B类,A类就可以使用B类的方法 ,A类的实例化对象可以调用B类的方法
继承有单继承和多继承 (在Java C#中是不允许多继承)
python是允许多继承的
class 父类2:
pass
class 父类1:
pass
class 子类(父类1,父类2):
pass
子类可以通过super来调用父类的属性/方法
class B(A):
def __init__(self, a=1, b=2, c=3):
super().__init__(a, b, c)
def ab(self):
super().ab()
def printab(self):
super().printab()
子类中出现和父类中某个方法同名的情况下,子类实例对象优先调用子类的方法
什么时候用继承,什么时候用组合
(1)多个子类具有相同的功能,可以把这些功能提取到基类中,然后子类们继承这个基类,这个时候就会用到继承
(2)将一个类拆成多个模块和一个主体,主体的字段接收到模块类的实例化对象,来通过字段来调用模块类的方法,这个时候就会用到组合
python的接口继承
python中的接口继承就是 父类只定义方法名和属性,不做具体的实现
子类继承父类然后重写父类的方法
默认情况下,在python中子类也可以不去重写父类的方法(也就是说父类没有对子类产生限制作用,而且使用alt+ins 也无法选择implement methods选项[这个选项在其他语言中就是实现接口方法的])
不过python中有一个abc的模块…
import abc
class A(ametaclass=abc.ABCMeta):
@abc.abstractmethod
def m1(self):
pass
@abc.abstractmethod
def m2(self):
pass
@abc.abstractmethod
def m3(self):
pass
@abc.abstractmethod
def m4(self):
pass
class B(A):
# alt+ins 选择的implement methods 然后选择所有的父类接口的方法
# 继承了接口类之后 如果没有重写所有的父类方法的话,当前类是无法进行实例化的
def m1(self):
pass
def m2(self):
pass
def m3(self):
pass
def m4(self):
pass
python中的继承顺序
python是可以多继承的 而Java C#只能单继承
python类如果继承了多个类
- 经典类多继承会遵循深度优先
- 新式类多继承会遵循广度优先
class AA:
pass
class A1(AA):
pass
class B1(AA):
pass
class A2(A1):
pass
class B2(B1):
pass
class C(A2, B2):
pass
# 如果是经典类的话 深度优先 C->A2->A1->AA 然后再找B2->B1 先一条路找到头 找不到的话换一条路
# 如果是新式类的话 广度优先 C->A2->B1 然后再找B2->B1 最后找AA
py3中的类都是新式类,按照广度优先找
类的继承顺序可以通过子类调用__mro__
来查看
mro的构造是通过一个叫c3线性算法来实现的 这个mro遵循三条规则
- 子类会优先于父类被检查
- 多个父类会根据mro中的顺序被检查
- 如果低层父类有a方法 高层父类也有a方法 子类在调用a方法的时候优先调用低层父类的a方法
# py3
(<class '__main__.C'>,
<class '__main__.A2'>, <class '__main__.A1'>,
<class '__main__.B2'>, <class '__main__.B1'>,
<class 'object'>)
# py2
# 最顶层的基类继承的object
(<class '__main__.C'>,
<class '__main__.A2'>, <class '__main__.A1'>,
<class '__main__.B2'>, <class '__main__.B1'>,
<type 'object'>) # 注意这里object是type类型而不是class
# 顶层类如果不继承object的话 子类是没有__mro__这个属性(经典类没有这个属性)
在子类中使用父类的方法
class AA:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def m1(self, a, b, c):
print(123)
class A1(AA):
def __init__(self, a, b, c, d, e):
super().__init__(a, b, c) # 子类调用的父类的init的初始化语句 这个init连self都不用传
# super(AA,self).__init__(a,b,c) 也可以这样写
self.d = d
self.e = e
def m1(self, a, b, c):
super().m1(a, b, c) # 子类调用父类的m1方法
print(self.a)
print(self.b)
print(self.c)
print(self.d)
print(self.e)
a1 = A1('a', 'b', 'c', 'd', 'e')
a1.m1(1, 2, 3)
# 运行程序
123
a
b
c
d
e
使用super()的话可以避免使用父类名
多态
其他语言中的多态是这样
父类 vf = new 子类()
vf.f1()
vf.f2()
# 这个时候父类类型的变量 vf只能调用 父类中存在且被子类重写的该父类的方法
# 如果vf想要调用子类的特有功能,需要强转为子类对象
子类 new_vf = (子类)vf
new_vf.z1()
new_vf.z2()
而python实例化对象不需要声明类的类型
实例化一个子类的对象,可以使用所有子类中能够调用的方法
所以 python中提多态是没有什么用的
封装
就是把东西装到一个容器中,然后封上
对外只提供一个接口访问
封装的本质就是明确区分内外,真正意义上的封装是内部实现逻辑,外部提供接口
既然提到了封装 就要说一下python中的权限修饰符
普通的成员方法和成员属性,都是可以直接从外部调用的(相对于java中public修饰符来说)
python中没有public和private这样的关键字
双下划线__属性/方法 可以起到private的作用
外部是无法直接通过对象获取私有属性的
如果要给私有属性赋值的话,相当于新增了一个属性
ab1 = A()
print(ab1.__dict__) # {'a': 1, '_A__b': 2, 'c': 3}
ab1.__b = 123
print(ab1.__dict__) # {'a ': 1, '_A__b': 2, 'c': 3, '__b': 123}
# 类中的私有属性 是_类__属性
严格意义上来说,python中是没有私有变量的(因为被重命名了)
在java和C#中 被private修饰的字段需要提供getXX和setXX来进行访问
在python中的话 也应该这么写
class A:
def __init__(self):
self.__a = 123
def get_a(self):
return self.__a
def set_a(self, a):
self.__a = a
a = A()
print(a.get_a()) # 123
a.set_a(20)
print(a.get_a()) # 20
魔术方法
对象.__dict__
打印字一个字典,字典里面包含所有对象可以调用的成员属性
(包括私有的成员属性)
打印一个类的__dict__
{'__module__': '__main__', 'c': 1, '__init__': <function A.__init__ at 0x000001B449FB57B8>, 'ab': <function A.ab at 0x000001B44B0329D8>, 'printab': <function A.printab at 0x000001B44B032A60>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
对象.__class__
print(ab1.__class__)
# 打印结果
<class '__main__.A'>
在类中可以通过self.__class__
来调用类方法/属性
其他
class C:
pass
class B(C):
pass
class A(B):
pass
print(A.__name__) #类的名字
print(A.__doc__) #类的文档字符串
print(A.__bases__) # (<class '__main__.B'>,) 所有父类构成的元组
# 出现多继承的情况 类似(<class '__main__.B'>, <class '__main__.D'>)
print(A.__dict__) # 类A的属性
print(A.__module__) # 类A所在的模块 如果当前文件是执行文件的话 会打印__main__
print(A.__class__) # 类作为实例对应的类 <class 'type'>
a = A()
print(a.__class__) # <class '__main__.A'>