问 题
类方法 get_access_token
调用父类实例方法 format()
报错。
报错信息
test_wework.py:15 (TestBaseApi.test_get_access_token)
self = <test_wework.TestBaseApi object at 0x00000194F9DB8B38>
def test_get_access_token(self):
> assert WeWork.get_access_token(self.secret) != ""
test_wework.py:17:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
cls = <class 'api.wework.WeWork'>
corpsecret = 'W1a-_z8wERVJRacyrzhuf5W4vHqBTkd2ghvTwAfivwI'
@classmethod
def get_access_token(cls, corpsecret):
r = requests.get(
"https://qyapi.weixin.qq.com/cgi-bin/gettoken",
params={
"corpid": cls._corpid,
"corpsecret": corpsecret
},
proxies=proxies,
verify=False
)
> cls.format(r.json())
E TypeError: format() missing 1 required positional argument: 'data'
..\api\wework.py:50: TypeError
源代码
class BaseApi:
def format(self, data):
print(json.dumps(data, indent=2))
class WeWork(BaseApi):
@classmethod
def get_access_token(cls, corpsecret):
r = requests.get(
"https://qyapi.weixin.qq.com/cgi-bin/gettoken",
params={
"corpid": cls._corpid,
"corpsecret": corpsecret
},
proxies=proxies,
verify=False
)
# 这里报错了!!!说我没有带data参数。。。我传了呀。。。
cls.format(r.json())
return r.json()["access_token"]
思 考
debuging…
-
实例方法调用父类实例方法没问题
class Department(BaseApi): _secret = "-tzpw8mha37Q7pDSfu22XbLg9EQ38Kx9Dr9aBsX94VU" def list(self): r = requests.get( "https://qyapi.weixin.qq.com/cgi-bin/department/list", params={"access_token": WeWork.get_token(self._secret)}, # headers={"content-type": "application/json; charset=UTF-8"}, headers={"content-type": "charset=utf-8"}, proxies=proxies, verify=False ) # 同样的语句,在非类方法中,可以正常调用format! self.format(r.json()) return r.json()
-
既然提示少一个参数,那我随便加个值吧
class BaseApi: def format(self, data): print(json.dumps(data, indent=2)) class WeWork(BaseApi): @classmethod def get_access_token(cls, corpsecret): r = requests.get( "https://qyapi.weixin.qq.com/cgi-bin/gettoken", params={ "corpid": cls._corpid, "corpsecret": corpsecret }, proxies=proxies, verify=False ) # 随便加了个空字符串,case可以跑通!!!! cls.format("", r.json()) return r.json()["access_token"]
我还是不满足,感觉还是有很多疑问…
疑 问
-
类实例方法的self代表什么意思?为什么类的实例方法之间互相调用不用带self?
self代表类实例对象本身。Python解释器看到self会自动将该方法和类对象绑定,实例方法之间可以隐式调用(不用带self)。
-
classmethod类方法中的cls又代表什么?
cls代表类对象本身
-
为什么类方法不能隐式调用实例方法?类方法调用实例方法的原理是什么?如何调用比较优雅?
定义类方法的时候,会默认带一个参数
cls
(可随意,默认是cls),代表类对象本身。Python解释器会自动绑定类属性和类方法,也就是说,类方法可以隐式调用类属性或者类方法(不用带cls)。比如,类方法
get_token()
调用类方法get_access_token()
,就不需要带cls参数
class WeWork(BaseApi):
_corpid = "ww84f2624b18176321"
_token = {}
@classmethod
def get_token(cls, secret):
# done: 需要保存token,不用每次调用都去请求
if secret not in cls._token.keys():
# 类方法之间可以隐式调用,不用带cls参数!!!!
cls._token[secret] = cls.get_access_token(secret)
cls.format("", cls._token)
return cls._token[secret]
# 类函数,直接通过类调用,不需要创建对象
@classmethod
def get_access_token(cls, corpsecret):
r = requests.get(
"https://qyapi.weixin.qq.com/cgi-bin/gettoken",
params={
"corpid": cls._corpid,
"corpsecret": corpsecret
},
proxies=proxies,
verify=False
)
cls.format("", r.json())
return r.json()["access_token"]
解决方案
如果类对象想要调用实例方法,分两种情况:
1. 如果需要用到self参数,则传入类实例对象进去
2. 如果不需要用到self参数,则可以随便传值
举个栗子
class Dream:
ambition = "Travel around the world"
def let_it_go(self):
# 引用了self,必须传入类实例对象
print(self.ambition)
print("Wish you make your dream come true.")
def i_have_a_dream(self):
print("I HAVE A DREAM")
@classmethod
def how_to_get(cls):
# TypeError: let_it_go() missing 1 required positional argument: 'self'
# cls.let_it_go()
# 传入类实例对象Dream()
cls.let_it_go(Dream())
# 没有用到self参数,可以随便传值
cls.i_have_a_dream("")
print("Just RUN!")
if __name__ == '__main__':
d = Dream()
# 传入Dream的类实例对象d
Dream.let_it_go(d)
# TypeError: let_it_go() missing 1 required positional argument: 'self'
# Dream.let_it_go()
Dream.how_to_get()
运行结果
Travel around the world
Wish you make your dream come true.
Travel around the world
Wish you make your dream come true.
I HAVE A DREAM
Just RUN!
Process finished with exit code 0