(一)面向对象编程
在面向对象中最重要的三个部分分别是:封装、继承、多态
可参考我前面的笔记:https://www.jianshu.com/p/92790574f3d2
在学习这些之前,我们先明白两个概念:类、对象
- 类(class):描述具有相同属性和方法的对象的集合。例如:人类。
- 对象(object):通过对类进行实例化,分配内存空间,能能够被用户使用。例如:张三。
# 类
class Person():
name="人类有个名字"
age="人类有年龄"
sex="人类有性别"
# 对象
p1 = Person()
p1.name = "我叫张三"
p1.age = 18
p1.sex = "man"
print(type(p1)) # <class '__main__.Person'>
(1)封装(encapsulation)
将一个类的所有属性和方法放到一起,或者说将数据和功能汇聚到一起。
封装能够保证类内部数据结构的完整性,只允许用户使用公开的数据,不允许使用私有数据,避免了外部对内部数据的影响,从而提高了程序的可维护性。
首先我们来看看,对象生命周期和函数执行的顺序:
self:代表当前对象,可以在类的内部直接访问任何类的变量和方法,类里面的所有函数都以self作为第一个参数。(self不是关键字,只是形参的名字)
class Person():
"""人类"""
# 这个方法一般不使用
def __new__(cls,*args, **kwargs):
print("new")
return super().__new__(cls)
# 一个人诞生了,需要给它起个名字,初始化年龄等
def __init__(self,name,age) -> None:
print("初始化函数") # 实例化对象的时候会自动调用
self.name = name
self.age = age
self.print_name()
def print_name(self):
print("我是:",self.name)
def __del__(self):
print("析构函数")# 释放对象的时候会自动调用
p1 = Person("张三", 18)
# new
# 初始化函数
# 我是: 张三
# 析构函数
类变量和实例变量
- 类变量:类变量是每一个类都有的变量,新创建的每一个实例的类变量都是一样的,地址也都一样。类变量可以是可变变量和不可变变量。我们可以以通过实例和类访问到类变量,如果类变量是可变变量,只要在一个实例中改变类变量,其他实例访问的时候也会改变。不推荐使用实例访问类变量。
- 实例变量:是每一个实例私有的,每个实例的变量的地址都不同,每个实例之间不会相互影响。并且无法通过类名访问。
私有变量:私有成员变量,外部不能访问。以__
开头
私有方法:私有成员方法,外部不能访问。以__
开头
理论上私有变量是不允许外部访问的,但是python给我们开了个后门,我们可以使用实例名._类名__变量名
来访问
class Person():
# 类变量
sex = "man"
list1 = [1,2,3]
__a = 1 # 私有成员变量
# 初始化函数
def __init__(self, name) -> None:
self.name = name
# 私有成员方法
def __get_a(self):
__a = 3 # 内部可以访问,外部不能访问
# 两个都叫张三的人
p1 = Person("张三")
p2 = Person("张三")
# 类变量, 不可变变量
print(Person.sex == p1.sex == p2.sex, id(Person.sex), id(p1.sex), id(p2.sex)) # True 1371018000456 1371018000456 1371018000456
p1.sex = "woman"
print(Person.sex == p1.sex == p2.sex, id(Person.sex), id(p1.sex), id(p2.sex)) # False 1371018000456 1371047207352 1371018000456
# 类变量, 可变变量
print(Person.list1 == p1.list1 == p2.list1, id(Person.list1), id(p1.list1), id(p2.list1)) # True 1371046436744 1371046436744 1371046436744
Person.list1.append(4)
print(Person.list1 == p1.list1 == p2.list1, id(Person.list1), id(p1.list1), id(p2.list1)) # True 1371046436744 1371046436744 1371046436744
# 实例变量
print(id(p1.name), id(p2.name)) # 1371047341288 1371047984448
print(Person.name) # AttributeError: type object 'Person' has no attribute 'name'
# 私有成员变量&方法
print(p1.__a) # AttributeError: 'Person' object has no attribute '__a'
print(p1.__get_a()) # AttributeError: 'Person' object has no attribute '__get_a'
print(p1._Person__a) # 1
类方法属于类但不属于实例的方法,需要在方法前面使用@classmethod
标记。
静态方法寄居在类中,独立存在,不能调用对象方法和类方法(类名.方法名除外),需要在方法前面使用@staticmethod
标记。
property 装饰器让一个方法被当成属性来调用,不能传入参数,,需要在方法前面使用@property
标记
python还有很多的魔术方法:
class A():
def __init__(self) -> None:
pass
@classmethod
def a(self):
print("类方法")
@staticmethod
def b(): # 无需传入self
print("静态方法")
@property
def c(self):
print("c")
def __str__(self) -> str:
return "hello, this is the object of A"
def d(self):
print("d")
a1 = A()
A.a() # 类方法
A.b() # 静态方法
A.c # c
A.d(self=1)# 可以使用类名调用实例方法但是需要手动传入self参数
a1.a() # 类方法
a1.b() # 静态方法
a1.c # c
a1.c() # c TypeError: 'NoneType' object is not callable
print(a1) # hello, this is the object of A
(2)继承
子类通过继承父类从而获取父类的属性和方法。
如果父类和子类有相同名称的方法或者属性,会被后者覆盖。可以通过super调用父类的重名函数。
一个类也可以继承于多个父类。
class Person(): # 父类
title="人类"
__a = "a"
def print_title(self):
print(Person.title)
class Teacher(Person): # 子类
name = "老师"
class Student(Person): # 子类
name = "学生"
def print_title(self):
print(Student.name)
def private_a(self):
print(Person._Person__a) # 不推荐使用
t1 = Teacher()
s1 = Student()
t1.print_title() # 人类 继承父类方法
s1.print_title() # 学生 覆盖父类方法
super(Student, s1).print_title() # 人类
# 访问父类私有属性
s1.private_a()
s1._Person__a # 'a' 不推荐使用
(3)多态
多态就是子类重写父类的方法,上面已经说过了。也就是子类和父类方法重名,子类方法覆盖父类方法。
class A():
def test(self):
print("a")
class B(A):
def test(self):
print("b")
b = B()
b.test() # b
网友评论