先说一些废话,距离上次更新隔了九天,主要是自己假期时间要学车考驾照,所有每天只能抽一些支离破碎的时间用来学习,导致学习效率很慢(之前也说过了,本人是一个对学习有洁癖的人(含贬义 狗头))所以导致学的很慢,不过这些也可能是我的借口,七天学python终究没有坚持下来,本人张合hhhh,在此宣布计划失败,不过之后仍会保持更新,最后祝7月20日生日的小伙伴们生日快乐!(是不是很突兀hhhh,我也这样觉得,但我就想说一句生日快乐!)
面向对象
面向对象是一种抽象化的编程思想
类和对象
类和对象的关系:用类去创建一个对象 实例化
-
类
类是对一系列具体相同特征和行为的事物的统称,是一个抽象的概念,不是真实存在的事物
特征即是属性,行为即是方法
class 类名(): ....
类名要满足标识符命名规则,同时遵循大驼峰命名习惯
-
对象
对象是类创建处理的真实存在的事物
先有类,再有对象
对象名 = 类名()
# 定义类 class Dog(): # 模拟小狗的测试 def __init__(self, name, age): # 初始化属性name和age self.name = name # 访问对象属性 初始化属性 self.age = age print(self) # <__main__.Dog object at 0x0000016AED023010> def sit(self): "模拟小狗蹲下" print(self.name.title() + " is now sitting") # 创建对象/实例化对象 my_dog = Dog('willie', 6) # 访问属性 print("my dog's name is " + my_dog.name.title()) # my dog's name is Willie print("my dog is " + str(my_dog.age) + ' years old') # my dog is 6 years old # 调用方法 print(my_dog.sit()) # Willie is now sitting print(my_dog) # <__main__.Dog object at 0x0000016AED023010> # 对象和self得到的内存地址相同,所以self指的是调用该函数的对象
类中的函数称为方法,类的方法与函数并无区别,所以仍可使用位置参数、默认参数、可变参数、关键字参数
其中,__ init _ _()是一个特殊的方法,每当根据类创建实例时,python会自动运行。该方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免python默认方法与普通方法发生名称冲突
self是一个指向实例本身的引用,让实例能够访问类中的属性和方法。创建实例时,将自动传入实参self,每个与类相关联的方法调用都自动传递实参self。self会自动传递,因此不需要传递它。
对象属性既可以在类外面添加和获取,也可以在类里面添加和获取。
类外面添加对象属性 语法:对象名.属性名=值
类外面获取对象属性 语法:对象名.属性名
类里面获取对象属性 语法:self.属性名
要访问实例的属性、调用方法时,可使用句点表示法。即指定实例的名称和要调用的方法和访问的属性,并用句点分隔。即
my_dog.name
my_dog.age
my_dog.sit()
一个类可以创建多个对象
魔法方法
在python中,__xx__
的函数叫做魔法方法,指的是具体特殊功能的函数
__init__()
作用:初始化对象
注意:
__init__()
方法,在创建一个对象时默认被调用,不需要手动调用__init__(self)
中self参数,不需要传递,python解释器会自动把当前的对象引用传递过去- 由于创建对象时
__init__()
会默认被调用,所以有__init__()
创建对象时,不能传入空的参数,必须传入与__init__()
匹配的参数
# 魔法方法
'''
1. 定义类
init魔法方法
2. 创建对象
3. 验证
'''
# 定义类
class Washer():
def __init__(self):
# 添加实例属性
self.width = 500
self.heigth = 800
def print_info(self):
# 调用实例属性
print(f'洗衣机的宽度是{self.width},洗衣机的高度是{self.heigth}')
# 创建对象
haier = Washer()
haier.print_info() # 洗衣机的宽度是500,洗衣机的高度是800
# 不需调用init函数便可直接运行,说明init函数已在创建对象时默认运行,不需要手动调用
# 带参数的init
class Washer():
def __init__(self,width,heigth):
# 添加实例属性
self.width = width
self.heigth = heigth
def print_info(self):
# 调用实例属性
print(f'洗衣机的宽度是{self.width},洗衣机的高度是{self.heigth}')
# 创建对象
haier = Washer(10,20)
haier.print_info() # 洗衣机的宽度是10,洗衣机的高度是20
geli = Washer(50,30)
geli.print_info() # 洗衣机的宽度是50,洗衣机的高度是30
__str()__
当使用print输出对象的时候,默认打印对象的内存地址。如果类定义__str__
方法,就会打印在这个方法中return的数据
# __str__
class Washer():
def __init__(self,width,heigth):
self.width = width
self.heigth = heigth
def __str__(self):
return '海尔洗衣机'
haier = Washer(20,10)
print(haier) # 海尔洗衣机
__del()__
当删除对象时,python解释器会默认调用__del__()
方法
# __del__
class Washer():
def __init__(self,width,heigth):
self.width = width
self.heigth = heigth
def __str__(self):
return '海尔洗衣机'
def __del__(self):
print(f'{self}已被删除')
haier = Washer(10,20)
# del haier
# 海尔洗衣机对象已被删除
# 不用手动删除,del同样会被调用 当代码运行完,内存释放,对象会被删除,所以del会被自动调用
继承
继承是一种创建新类的方式,新建的类可称为子类或派生类,父类可称为基类或超类
python面向对象的继承指的是各个类之间的所属关系,即子类默认继承父类的所有属性和方法
python支持多继承,新建的类可以支持一个或多个父类
# 定义父类
class A():
def __init__(self):
self.num = 1
def pri_info(self):
print(self.num)
# 定义子类
class B(A):
pass
# 查看父类默认继承的类
print(A.__bases__) # (<class 'object'>,) python3中默认继承object类
# 查看子类B继承的父类
print(B.__bases__) # (<class '__main__.A'>,)
# 测试
result = B()
result.pri_info() # 1 说明子类会继承父类的全部属性和方法
经典类与新式类
python2有经典类与新式类的区别:
经典类:没有继承object类的子类,以及该子类的子类、子子类。不由任意内置类型派生出的类
新式类:继承object类的子类,以及该子类的子类、子子类
class 类名:
代码 # 经典类
class 类名(object):
代码 # 新式类
在python3中,没有继承任何类,默认继承object类,object类是所有类的顶级类,所以python3中都是新式类
单继承
class Animal():
def run(self):
print('Animal is running')
class Cat(Animal):
pass
cat1 = Cat()
cat1.run() # Animal is running
多继承
多继承即一个类可同时继承多个父类
class A():
def print_info(self):
print('A')
class B():
def print_info(self):
print('B')
class C(A, B):
pass
# 查看C的父类
print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B'>)
# A和B都是C的父类
c1 = C()
c1.print_info() # A
# 当一个类中有多个父类时,默认优先使用第一个父类的同名属性和方法
注意:当一个类有多个父类时,默认使用第一个父类的同名属性和方法
多层继承
python支持多层继承
class A:
pass
class B(A):
pass
class C(B):
pass
继承的查找顺序
对象->子类->直接父类->间接父类
class Foo():
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj = Bar()
obj.f2()
'''
结果:
Foo.f2
Bar.f1
'''
多继实现原理(mro顺序)
-
菱形结构
D继承B和C,B和C分别继承A,这就是一个菱形结构
如果属性或方法重名,输出的顺序是按mro列表输出的顺序继承
class A(): def test(self): print('A') class B(A): def test(self): print('B') class C(A): def test(self): print('from C') class D(B,C): pass obj = D() obj.test() # 结果----> B ''' 可以打印出mro列表查看顺序''' print(D.__mro__) # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
注意:
1.
__mro__
也可写成mro(),即类名.mro()。前者返回一个元组,而后者返回一个列表。调用mro方法必须是起始类2.mro通过一个C3线性算法实现
3.mro查找准则:1.先查子类,再查父类2.当继承多个父类时,按mro列表顺序查找
-
非菱形结构
class E: pass class F: pass class B(E): pass class C(F): pass class D: def test(self): print('D') class A(B, C, D): pass print(A.__mro__) # (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>) obj = A() obj.test() # D
经典类按深度优先算法查询
新式类按广度优先算法查询
子类重写父类同名属性和方法
子类和父类拥有同名属性和方法,子类创建对象调用时,调用的是子类的同名属性和方法
所以,当对于父类的方法,子类并不需要时,可对其进行重写,在子类中定义与要重写的父类方法同名。这样,python将不会考虑父类方法,而只关注子类中定义的相应方法。
super()调用父类方法
super()方法就是为了解决多重继承的问题,在一个父类中使用super()方法用来调用下一个父类的方法
super()方法有两种写法:1.super(当前类名,self).函数名() 2. super().函数()
class A():
def test(self):
print('A')
class B(A):
def test(self):
print('B')
super().test()
class C(B):
def test(self):
super().test()
c1 = C()
c1.test()
'''
结果为:
B
A
'''
class A():
def test(self):
print('A')
super().test()
class B():
def test(self):
print('B')
class C(A, B):
pass
c1 = C()
c1.test()
'''
结果为:
A
B
'''
访问限制(私有属性和方法)
在class内部,可定义属性和方法,而外部代码可通过直接调用实例变量的方法来提取数据,这样就隐藏内部的复杂逻辑
python可为实例属性和方法设置私有权限,即设置某个实例属性或实例方法不继承给子类,或不被外部访问
设置私有权限的方法:在属性名和方法名 前面 加上两个下划线__
class A():
def __init__(self):
self.__name = '小明'
self.age = 16
def getname(self): # 获取私有属性
return self.__name
def __getinfo(self):
return '无法获取私有属性及方法'
a = A()
print(a.age) # 16
print(a.__name) # 外部无法访问私有属性和私有方法
# 报错 AttributeError: 'A' object has no attribute '__name'. Did you mean: '_A__name'?
print(a.getname()) # 小明 可通过定义其他函数在内部获取私有属性
a.__getinfo() # 报错 AttributeError: 'A' object has no attribute '__getinfo'. Did you mean: '_A__getinfo'?
# 创建子类继承父类A
class B(A):
pass
# 私有属性和方法不能被继承
b = B()
print(b.age) # 16
print(b.__name) # 报错 AttributeError: 'B' object has no attribute '__name'. Did you mean: '_A__name'?
# 并未继承到私有属性和私有方法
b.__getinfo() # 报错
面向对象三大特性
-
封装
即将属性和方法写到类的内部的操作即为封装
封装可以为属性和方法添加私有权限
-
继承
子类默认继承父类的所有属性和方法
子类可以重写父类属性和方法
-
多态
传入不同的对象,产生不同的结果
多态
多态指的是一类事物有多种形态(一个抽象类有多个子类,因此多态概念依赖于继承)
多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
class Animal():
def run(self):
print('Animal is running')
class Dog(Animal): # 子类重写父类方法
def run(self):
print('Dog is running')
class Cat(Animal): # 子类重写父类方法
def run(self):
print('Cat is running')
def run_oneself(animal): # 实例可用作属性,即将实例传入参数
animal.run()
# 两种写法
a = Dog()
# 第一种
run_oneself(a) # Dog is running
# 第二种
run_oneself(Cat()) # Cat is running
run_oneself(Animal()) # Animal is running
# 传入不同的实例,执行的结果也不同
类属性和实例属性
类属性
类属性是类对象所拥有的属性,它被该类的所有实例对象所共有
类属性可以使用类对象或实例对象访问
类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建一个实例属性
类属性的优点
记录的某项数据始终保持一致时,则定义类属性
实例属性要求每个对象为其单独开辟一份内存空间记录数据,而类属性为全类所共有,仅占用一份内存,节省内存空间
class A():
# 设置类属性
num = 10
a1 = A()
a2 = A()
# 访问类属性
print(A.num) # 10
print(a1.num) # 10
print(a2.num) # 10
# 修改类属性
A.num = 14
print(A.num) # 14
print(a1.num) # 14
print(a2.num) # 14
a1.num = 20
print(A.num) # 14
print(a1.num) # 20
print(a2.num) #14
# 不能通过实例修改类属性,如果这样操作,实则是创建一个实例属性
类方法和静态方法
-
类方法
需要用修饰器
@classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数当方法中需要使用类对象(如访问私有类属性等时,定义类方法)
类方法一般和类属性配合使用
class A(): __a = 10 @classmethod def get__a(cls): return cls.__a a = A() result = a.get__a() print(result) # 10
-
静态方法
需要通过装饰器
staticmethod
来进行修饰,静态方法既不需要传递类,也不需要传递实例对象(形参没有self/cls)静态方法能够通过实例对象和类对象去访问
当方法中既不需要使用实例对象、也不需要使用类对象时,定义静态方法
取消不需要的参数传递,有利于减少不必要的内存占用和性能消耗
class A(): @staticmethod def info_print(): print('A') a = A() # 静态方法既可以使用对象访问又可以使用类访问 a.info_print() # A A.info_print() # A
常用方法总结
方法名称 | 功能 |
---|---|
sel.__class__ | 查看对象所属类 |
类名/对象名.__dict__ | 查看类/对象名称空间 |
类名/对象名.__bases__ | 查看父类 |
起始类名.__mro__ | 打印继承顺序 |
locals() | 查看局部名称空间 |
globals() | 查看全局名称空间 |
dirs(str) | 查看字符串所搭配的内置方法有哪些,查看内容可换 |