Python面向对象编程深度解析
文章目录
一、面向对象基本语法
1、面向对象的核心思想
2、面向对象基本语法
创建类和对象:
创建类:class 类名, 类名要用驼峰式(单词首字母大写)
创建对象:类名()
# 创建类
class Car:
print('我是造车图纸')
# 创建对象
target = Car() # 创建对象的时候会自动执行类里面的内容
-------------------------------------
我是造车图纸
类中有两样内容
属性:用来初始化一个对象
动作:用来创建一个对象所拥有的功能,常被写成函数(类中的函数被称作方法)
# 创建类,类名:Car
class Car:
# 创建属性__init__(self),用来初始化这个对象:通过self来初始化对象中的变量,self相当于js/php中的this
def __init__(self, color, name): # color,type就是对象中的变量
print('我是属性')
# self给车添加属性
self.color = color # 被初始化的变量就叫做实例变量,比如:self.color
self.type = name
slef.age = 23 # 可以自定义一个初始化变量
# 创建动作(方法),用来描述对象所拥有的功能
def run(self):
print('我能跑')
# 创建对象,把参数传给属性
target = Car('红色','奔驰')
访问类中的方法
self.方法(self就是target)
# 创建类,类名:Car
class Car:
# 创建属性__init__(self),用来初始化这个对象:通过self来初始化对象中的变量
def __init__(self, color, name): # color,type就是对象中的变量
print('我是属性')
print('self对象的ID:',self)
# self给车添加属性
self.color = color # 被初始化的变量就叫做实例变量,比如:self.color
self.type = name
# 创建动作(方法),用来描述对象所拥有的功能
def run(self):
print('我能跑')
# 创建对象target,对象负责把值传给属性__init__并进行初始化,属性中的self就是target对象,它们的id一样
target = Car('红色','奔驰')
print('terget对象的ID:',target)
# 使用对象访问类中的方法
target.run()
-------------------------------
我是属性 # 默认会执行属性__init__(self)里面的内容
self对象的ID: <__main__.Car object at 0x0000012DF92C4828> # 说明self就是target
terget对象的ID: <__main__.Car object at 0x0000012DF92C4828>
我能跑 # 调用类中的方法
3、造对象的过程(原理)
我们创建类的时候,默认生成一个__new__,但是不显示
当我们使用Foo() 去创建一个对象的时候,会优先执行__new__,并在__new__里面开辟一块内存(相当于开了一块地),然后__init__在这个内存地址里面进行初始化对象的操作。
二、成员与类变量
1、成员与类变量
实例变量:被对象初始化过的变量,通过对象调用:【对象.变量】
类变量:在类中定义的变量,作用于整个类,通过类名调用:【类名.变量】
class Car:
age = 23 # 类变量,作用于整个类中
def __init__(self, color, name):
self.color = color # 实例变量,被初始化的变量,比如:self.color
self.type = name
def run(self):
print('我能跑')
# 创建对象
target = Car('红色','奔驰')
print(target.color) # 通过对象去访问实例变量,就相当于self.color
print(Car.age) # 通过类名访问类变量
--------------------------------------------
红色
23
三、方法
一、方法,其实就是类中的函数。
1、实例方法
通过对象调用,【对象.方法】
哪个对象调用这个方法,self就是那个对象
class Car:
age = 23
def __init__(self, color, name):
self.color = color
self.type = name
# 创建实例方法
def run(self):
# 通过对象调用实例变量,通过类名调用类变量
print(f'我叫{self.type},我今年{Car.age}岁了,我是{self.color}的')
# 创建对象
target = Car('红色','奔驰')
# 调用实例方法 [对象.方法]
target.run()
-------------------------------------
我叫奔驰,我今年23岁了,我是红色的
1.1 验证这句话【哪个对象正在调用这个方法,self就是那个对象】
class Func:
def fangfa(self):
print(self) # 打印self
F = Func() # 造对象
F.fangfa() # 用对象访问方法
print(F) # 打印对象 print(Func())
-------------------------------------------------
<__main__.Func object at 0x000001CE899452B0> # 打印的self
<__main__.Func object at 0x000001CE899452B0> # 打印的对象F
2、类方法
通过类名调用, 类名.方法
必须在上面加上一个@classmethod装饰器,第一个参数是cls,cls接收一个类名(cls==类名)
一般用来创建对象: cls() == 类名()
哪个类名正在调用这个类方法,cls就是那个类名
class Car:
age = 23
def __init__(self, color, name):
self.color = color
self.type = name
# 创建实例方法
@classmethod
def run(cls,var1,var2): # 里面可以带参数
print(f'我是类方法{var1}{var2}')
print('打印cls:',cls)
# 调用类方法 [类名.方法]
Car.run('哈哈','呵呵')
print('打印类名:',Car) # 类名ID与clsID一样,所以类名==cls
-------------------------------------------------
我是类方法哈哈呵呵
打印cls: <class '__main__.Car'>
打印类名: <class '__main__.Car'>
2.1 验证这句话[哪个类名正在调用这个类方法,cls就是那个类名]
class Func:
@classmethod
def lei(cls):
print(cls) #打印cls
Func.lei() # 调用类方法
print(Func) # 打印类名
-----------------------------------
<class '__main__.Func'> # 打印cls
<class '__main__.Func'> # 打印类名
2.2、类方法使用例子
# 创建类
class User:
# 初始化对象
def __init__(self, username, password):
self.username = username
self.password = password
# 实例方法
def names(self):
print(f'my name is {self.username}')
# 类方法
@classmethod
def login(cls):
while 1:
username = input('请输入用户名:').strip()
password = input('请输入密码').strip()
if username == 'alex' and password == '123456':
return cls(username, password) # 创建对象,cls就是类名(相当于User(username,password)
else:
print('用户名或密码错误')
u = User.login() # 通过执行类方法来创建对象,User.login()返回值是 cls(username,password)==User(username,password)
u.names() # 通过对象调用实例方法
---------------------------------------------
请输入用户名:alex
请输入密码123456
my name is alex
3、静态方法
通过类名调用
就是在类里面写的一个函数,必须在上方加上一个@staticmethod装饰器,参数无要求
一般用于做一些算数逻辑
class Car:
age = 23
def __init__(self, color, name):
self.color = color
self.type = name
# 创建实例方法
@staticmethod
def run(var): # 里面可以带参数
print(f'我是静态方法{var}')
# 调用静态方法 [类名.方法]
Car.run('haha')
----------------------------------------
我是静态方法haha
4、属性方法
本质:把属性方法的返回值变成一个实例变量
访问的时候是通过 【对象.方法】访问就像是在访问实例变量,而不是【对象.方法()】
用途:实时计算一个东西
from datetime import datetime
class Person:
# 类变量,获取今年年份
times = int(datetime.now().strftime("%Y"))
def __init__(self, birth_year):
self.birth_year = birth_year
# self.age = Person.times - self.birth_year #本来应该是这样去计算年龄的
# 属性方法,可以实时去计算年龄
@property # 负责把一个方法改成实例变量,把这个方法的返回值变成了self.age
def age(self):
print('你今年的年龄是:')
return Person.times - self.birth_year # 类变量-实例变量
year = int(input('请输入你的出生年份:'))
p = Person(year) # 造对象
print(p.age) # 调用属性方法,注意方法名不加括号(就相当于在调用 self.age)
---------------------------------------
请输入你的出生年份:1991
你今年的年龄是:
28
4.1、给属性方法重新赋值
class Person:
def __init__(self,money):
self.money = money
#print(self.money)
# 属性方法
@property
def prize(self):
return '本次累计奖金是:', 20000 + self.money
# 属性方法的分身
@prize.setter # 修改上一个方法的返回值,把一个新的值带进去重新计算
def prize(self, var1):
self.var1 = var1 # self.var1 把新的变量初始化成实例变量
print('下次累计奖金是', 20000 + self.var1)
self.money = 20000 + self.var1 # 还可以把改变的结果重新赋值给实例变量
p = Person(500) # 创建对象
print(p.prize) # 打印第一个prize的返回值
p.prize = 1000 # 我对第一个prize返回值不满意,所以通过@prize.setter给var1传入一个新的值,去改变prize的结果,再次打印新的prize的返回值
----------------------------------------------
('本次累计奖金是:', 20500)
下次累计奖金是 21000
5、在一个类中调用另一个类中的变量
# 在Person类中调用Func类中的name
class Func:
def __init__(self, name):
self.name = name
class Person:
def __init__(self,age):
self.age = age # self.age 就等于对象 Func('alex')
def test(self):
print(self.age.name) # 在Person类中调用Func类中的name, 就等于执行 Func('alex').name
F = Func('alex') # 造对象,把'alex'给变量name
P = Person(F) # 造对象,把Func('alex')这个对象给变量age
-----------------------------------------
alex
6、在一个方法中调用另一个方法中的变量
如果一个类下面的函数都需要去执行某个变量,要么把这个变量写成全局变量,通过global去调用,要么放到init里面,要么就使用下面的方法把这个函数专门当成变量函数
函数里面的变量只能作用于当前函数中使用,别的函数不能去调用
一个函数最好只办一件事,不用把所有功能都写到一个函数中,不然会出问题的。
(1) 写法一
class Func:
# 这个函数专门是用来获取age的,里面不要放其他代码
def test1(self):
age = input('请输入年龄:') # 定义全局变量,这个变量无法在外面访问
self.age = age # 只能先把全局变量转换成实例变量
def test2(self):
# self.test1() # 在这里先执行一个test1也可以
print(self.age) # 这边才能够拿到self.age
F = Func() # 造对象
F.test1() # 必须先执行一次test1
F.test2()
---------------------------------------
请输入年龄:23
23
(2)写法二
class Func:
# 这个函数专门是用来获取age的,里面不要放其他代码
def test1(self):
age = input('请输入年龄:') # 定义全局变量,这个变量无法在外面访问
self.age = age # 只能先把全局变量转换成实例变量
return self.age # 作为return返回值
def test2(self):
print(self.test1()) # 执行test1()并打印return返回值
F = Func() # 造对象
F.test2()
-----------------------------------
请输入年龄:23
23
(3)写法三
class Func:
def test1(self):
self.name = 'alex'
self.test2() # 先执行一遍test2()
def test2(self):
print('哈哈',self.name) # 可以直接使用test1()里面的变量
F = Func()
F.test1() # 调用test1()方法
----------------------------------
哈哈 alex
7、在一个类的方法中调用另一个类中的方法中的变量
class Func:
# 实例方法
def test(self):
self.age = 111
class Func2(Func): # 继承
def test2(self):
self.test() # 先把test()方法引用进来
print(self.age) # 再去打印test()中的变量
F = Func2() # 创建Func2()的对象
F.test2() # 执行test2()方法
四、私有
1、私有
__开头的内容都是私有
私有内容只能在类里面访问,不允许在类外面访问
class Person:
__names = 'Alex' # 私有类变量
def __init__(self,name,age):
self.__name = name # 初始化成私有实例变量
self.__age = age
def discrib(self):
print('my name is %s' %(self.__name)) # 私有实例变量只能通过%s使用
print(f'My NAME Is {self.__name}') # 私有实例变量也可以这么用(pycharm会提示,不用管它)
print(f'my name is %s' %(Person.__names)) # 私有实例变量不能这么用
p = Person('oldboy','23')
p.discrib()
print(p__name) # 这样是不行的
----------------------------------
my name is oldboy
My NAME Is oldboy
my name is Alex
五、特殊成员
1、这里只是稍微简单介绍一下,一般很少能用到
特殊成员只有在特殊情况下才会被访问. 一般我们很少主动去访问这些内容. 都是自动调用的.
比如, 我们在创建对象的时候, 会默认执行__init__()方法
init: 创建对象的时候执行__init__
new: 创建对象的时候执行__new__
call: 当执行 对象() 的时候执行__call__
特殊成员只有在特殊情况下才会被访问. 一般我们很少主动去访问这些内容. 都是自动调用的.
比如, 我们在创建对象的时候, 会默认执行__init__()方法
class Foo:
def __init__(self):
print('默认也会访问我')
def __new__(cls,*args, **kwargs):
print('默认先会访问我')
return super().__new__(cls)
def __call__(self, *args, **kwargs):
print('默认最后访问我')
f = Foo() # 创建对象时,默认会先执行__new__,后执行__init__
print('---------------')
f() # 把对象当成函数一样去执行,默认会执行到__call__
getitem
settiem
delitem
class Foo:
def __init__(self):
self.dic = {} # 创建一个空字典
# 用于添加
def __setitem__(self, key, value):
print('我是setitem')
print(key, value)
self.dic[key] = value # 调用实例变量,把用户输入的key value 写入字典中
# 用于查询
def __getitem__(self, item):
print('我是getitem')
return self.dic.get(item) # 调用实例变量,去字典中查询item对应的值
# 用于删除
def __delitem__(self, key):
print('我是delitem')
self.dic.pop(key) # 调用实例变量,删除字典中的key
f = Foo() # 创建对象
f["name"] = "alex" # 调用__setitem__方法,把"name"给key,把"alex"传给value,并写入字典
print(f["name"]) # 调用__getitem__方法, 把"name" 传给item,并进行查询
del f['name'] # 调用__delitem__方法删除字典里面的key
print(f.dic) # 查看字典中的内容
----------------------------------------------
我是setitem
name alex
我是getitem
alex
我是delitem
{}
六、继承
一、继承
作用:减少代码量,让父类作为公共代码供子类调用
1、单继承
子类可以继承父类中私有内容 (__开头的) 以外的所有内容
class Animal: # 父类
def cry(self):
print('我会汪汪叫')
class Cat(Animal): # 子类继承父类,子类是Cat, 父类是Animal
def catch_mouse(self):
print('我会抓老鼠')
p = Cat() # 创建子类的对象
p.catch_mouse()
p.cry() # 由于子类继承了父类,所以子类对象也可以调用父类里面的方法
----------------------------------------------
我会抓老鼠
我会汪汪叫
子类中可以有和父类重名的方法,优先会执行子类中的方法
class Animal: # 父类
def cry(self):
print('我会汪汪叫')
def like(self):
print('我最爱吃肉骨头')
class Cat(Animal): # 子类继承父类,子类是Cat, 父类是Animal
def catch_mouse(self):
print('我会抓老鼠')
def cry(self):
print('我会喵喵叫')
p = Cat() # 创建子类的对象
p.catch_mouse()
p.cry() # 如果子类里面的方法和父类里面的方法重名,优先执行子类里面的方法
p.like()
---------------------------------
我会抓老鼠
我会喵喵叫
我最爱吃肉骨头
2、多继承
class Father1: # 父类1
def apple(self):
print('Father1可以给你买苹果')
def play(self):
print('Father1可以陪你玩')
def car(self):
print('Father1开法拉利陪你玩')
class Father2: # 父类2
def orange(self):
print('Father2可以给你买橘子')
def play(self):
print('Father2可以陪你玩')
def car(self):
print('Father2开凯迪拉克陪你玩')
class Son(Father1, Father2): # 子类继承2个父类,执行顺序:就近原则
def play(self):
print('自己和自己玩')
p = Son() # 造子类对象
p.apple() # 由于子类继承了父类,所以子类对象可以调用父类中的方法
p.orange()
p.play() # 出现同名方法,优先执行子类中的方法
p.car() # 父类中出现同名方法,优先执行最近的父类中的方法
--------------------------------
Father1可以给你买苹果
Father2可以给你买橘子
自己和自己玩
Father1开法拉利陪你玩
3、super()
如果子类/父类中的方法执行完,还想继续执行下一个类中的方法就用super()
class Father1:
def play1(self):
print('Father1开飞机陪你玩')
def money(self):
print('Father1每天给10000块钱')
class Father2:
def play2(self):
print('Father2开飞船陪你玩')
def play1(self):
print('Father2开飞碟陪你玩')
def money(self):
print('Father2每天给1块钱')
super().money() # 继续执行下一个类中的money()
class Son(Father2,Father1): # 就近原则
def like(self):
print('哈哈哈')
super().play2() # 执行完子类中的方法后继续执行下一个类中的play2()方法
super().play1() # 执行完子类中的方法后继续执行下一个类中的play1()方法
# super(Son, self).play1() # 也可以写成这样,直接tab补全就行,意思是执行Son后面的类中的方法
p = Son() # 造子类对象
p.like() # 先找子类中的方法并执行
p.money() # 如果子类中没有这个方法就执行父类中的方法,先去Father2中找
print(Son.mro()) # 如果继承关系很复杂可以通过mor() 查看优先执行顺序
----------------------------------------------------
哈哈哈
Father2开飞船陪你玩
Father2开飞碟陪你玩
Father2每天给1块钱
Father1每天给10000块钱
[<class '__main__.Son'>, <class '__main__.Father2'>, <class '__main__.Father1'>, <class 'object'>]
4、super()的继承原理
class Father:
def __init__(self, address): # (4) 这里的self == s对象,并初始化s对象中的address
self.address = address
class Son(Father):
def __init__(self, address): # (2)初始化对象,self==s对象
super(Son, self).__init__(address) # (3)执行父类中的__init__,这里的self是子类中的self传过来的==s对象,并把address传给父类
#super().__init__(address) # 或写成这样
s = Son('北京') # (1)创建对象
print(s.address)
----------------------------------------------
北京
七、反射
一、反射(就是方便在某一个对象中去寻找某一个功能并执行)
getattr 可以从模块、类、对象中获取功能,在python中一切皆对象,所以说getattr可以从对象中获取功能
getattr(对象, 功能):在某一个模块中去寻找某个功能 (get表示找,attr表示功能)
hasattr(对象,功能):判断模块中是否有某个功能
callable() 判断某个功能能否被调用(只有函数、类、对象能被调用)
1、定义一个模块mode12.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 这是创建的模块mode12
name = 'alex'
# 功能
def play():
print('正在玩')
def run():
print('正在跑')
def eat():
print('正在吃')
2、getattr() 用法
import mode12 # 引入定义的模块
fn = getattr(mode12, "run") # 在mode12模块中找run这个功能(注意用引号引起来),并赋值给fn
fn() # 找到后就去执行这个功能
-----------------------------------------------------
正在跑
3、hasattr() 用法
(1) 如果mode12模块中有run功能,则返回True,否则返回False
import mode12
print(hasattr(mode12, "run"))
------------------------------
True
(2) hasattr 结合 getattr 使用
import mode12
if hasattr(mode12, "run"): # 判断mode12模块中是否有这个功能
fn = getattr(mode12, "run") # 如果有这个功能,就获取这个功能
fn() # 并执行这个功能
else:
print('没有找到这个功能')
4、callable()用法
import mode12
gn = input('请输入查找的功能:')
# 判断mode12模块中是否有这个功能,这里的功能需要用""引起来
if hasattr(mode12, gn):
# 如果有这个功能,就获取这个功能
fn = getattr(mode12, gn)
# 判断这个功能是否能被调用(函数、类、对象都可以被调用)
if callable(fn):
fn() # 能被调用就执行这个功能
else:
print(fn) # 不能被调用就打印这个功能(比如变量)
else:
print('没有找到这个功能')
------------------------------------
请输入查找的功能:run
正在跑
5、用getattr() 从类中获取功能
class Func:
name = 'alex'
# 功能
def play(self):
print('正在玩')
@classmethod
def run(cls):
print('正在跑')
# 调用类里面的实例方法
fn = getattr(Func, 'play')
print(hasattr(Func, 'play'))
fn('') # 里面必须加点东西
# 调用类里面的类方法
fc = getattr(Func, 'run')
fc() # 里面不用加东西
---------------------------------------
True
正在玩
正在跑
6、用getarrt() 从对象中获取内容
class Func:
name = 'alex'
# 功能
def play(self):
print('正在玩')
@classmethod
def run(cls):
print('正在跑')
# 造对象
F = Func()
# 从对象中获取实例方法
print(hasattr(F, 'play'))
fn = getattr(F, 'play')
fn() # 这里相当于F.play()
# 从对象中获取类方法
fn = getattr(F, 'run')
fn() # 这里相当于F.run()
# 给对象创建一个新的变量,内容是'girl' (创建在内存里面)
setattr(F, 'hobby', 'girl') # setattr(对象,方法, 内容),用的少
print(F.hobby)
--------------------------------------
True
正在玩
正在跑
girl
八、约束
一、约束
子类继承父类后,必须让子类重新某个方法
1、原理
class Father:
def sleep(self):
raise NotImplementedError('你需要自己创建这个方法')
class Son(Father): # 子类继承父类
pass
# 如果子类想调用父类中的sleep()方法,就会报错,必须自己去创建sleep()方法
S = Son()
S.sleep()
------------------------------
NotImplementedError: 你需要自己创建这个方法
九、三元表达式
1、三元表达式
条件成立返回a,否则返回b
# 返回最大的值
a = 1
b = 2
c = a if a>b else b # 如果a>b就返回a,否则返回b
print(c)
-----------------------------
2
作者:会飞的爱迪生