第八篇 python 面向对象编程
11 面向对象编程
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
11.1 类和对象
面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
11.1.1 类
人以类聚 物以群分。具有相似内部状态和运动规律的实体的集合(或统称为抽象)。 具有相同属性和行为事物的统称
类是抽象的,在使用的时候通常会找到这个类的一个具体的存在,使用这个具体的存在。一个类可以找到多个对象
11.2.2 对象
某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的。可以是直接使用的
11.2.3 类和对象的关系
类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;
11.2.4 定义类
定义一个类,格式如下:
class 类名: 方法列表
例:定义一个Car类
# 定义类class Car: # 方法 def getCarInfo(self): print('车轮子个数:%d, 颜色%s'%(self.wheelNum, self.color)) def move(self): print("车正在移动...")
说明:
- 定义类时有2种:新式类和经典类,上面的Car为经典类,如果是Car(object)则为新式类
- 类名 的命名规则按照"大驼峰"
11.2.5 创建对象
通过上一节,定义了一个Car类;就好比有车一个张图纸,那么接下来就应该把图纸交给生成工人们去生成了
python中,可以根据已经定义的类去创建出一个个对象
创建对象的格式为:
对象名 = 类名()
创建对象demo:
# 定义类class Car: # 移动 def move(self): print('车在奔跑...') # 鸣笛 def toot(self): print("车在鸣笛...嘟嘟..")# 创建一个对象,并用变量BMW来保存它的引用BMW = Car()BMW.color = '黑色'BMW.wheelNum = 4 #轮子数量BMW.move()BMW.toot()print(BMW.color)print(BMW.wheelNum)
运行结果:
车在奔跑...车在鸣笛...嘟嘟..黑色4
11.2.6 __init__()
方法
1)使用方式
def 类名: #初始化函数,用来完成一些默认的设定 def __init__(): pass
2)__init__()
方法的调用
# 定义汽车类class Car: def __init__(self): self.wheelNum = 4 self.color = '蓝色' def move(self): print('车在跑,目标:夏威夷')# 创建对象BMW = Car()print('车的颜色为:%s'%BMW.color)print('车轮胎数量为:%d'%BMW.wheelNum)
运行结果:
车的颜色为:蓝色车轮胎数量为:4
11.2.7 魔法方法
1)打印id()
如果把BMW使用print进行输出的话,会看到如下的信息,例:
# 定义汽车类class Car: def __init__(self): self.wheelNum = 4 self.color = '蓝色' def move(self): print('车在跑,目标:夏威夷')# 创建对象BMW = Car()print(BMW)
运行结果:
即看到的是创建出来的BMW对象在内存中的地址
2) 定义__str__()
方法
class Car: def __init__(self, newWheelNum, newColor): self.wheelNum = newWheelNum self.color = newColor def __str__(self): msg = "嘿。。。我的颜色是" + self.color + "我有" + str(self.wheelNum) + "个轮胎..." return msg def move(self): print('车在跑,目标:夏威夷')BMW = Car(4, "白色")print(BMW)
运行结果:
嘿。。。我的颜色是白色我有4个轮胎...
3)定义__del__()
方法
创建对象后,python解释器默认调用__init__()
方法;
当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()
方法
import timeclass Animal(object): # 初始化方法 # 创建完对象后会自动被调用 def __init__(self, name): print('__init__方法被调用') self.__name = name # 析构方法 # 当对象被删除时,会自动被调用 def __del__(self): print("__del__方法被调用") print("%s对象马上被干掉了..." % self.__name)# 创建对象dog = Animal("哈皮狗")# 删除对象del dogcat = Animal("波斯猫")cat2 = catcat3 = catprint("---马上 删除cat对象")del catprint("---马上 删除cat2对象")del cat2print("---马上 删除cat3对象")del cat3print("程序2秒钟后结束")time.sleep(2)
运行结果:
__init__方法被调用__del__方法被调用哈皮狗对象马上被干掉了...__init__方法被调用---马上 删除cat对象---马上 删除cat2对象---马上 删除cat3对象__del__方法被调用波斯猫对象马上被干掉了...程序2秒钟后结束
- 当有1个变量保存了对象的引用时,此对象的引用计数就会加1
- 当使用del删除变量指向的对象时,如果对象的引用计数不会1,比如3,那么此时只会让这个引用计数减1,即变为2,当再次调用del时,变为1,如果再调用1次del,此时会真的把对象进行删除
总结
- 在python中方法名如果是
__xxxx__()
的,那么就有特殊的功能,因此叫做“魔法”方法 - 当使用print输出对象的时候,只要自己定义了
__str__(self)
方法,那么就会打印从在这个方法中return的数据
常用的魔法方法列表
魔法方法 | 含义 |
---|---|
基本的魔法方法 | |
_new_(cls[, …]) | 1. new 是在一个对象实例化的时候所调用的第一个方法 2. 它的第一个参数是这个类,其他的参数是用来直接传递给 init 方法 3. new 决定是否要使用该 init 方法,因为 new 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 new 没有返回实例对象,则 init 不会被调用 4. new 主要是用于继承一个不可变的类型比如一个 tuple 或者 string |
_init_(self[, …]) | 构造器,当一个实例被创建的时候调用的初始化方法 |
_del_(self) | 析构器,当一个实例被销毁的时候调用的方法 |
_call_(self[, args…]) | 允许一个类的实例像函数一样被调用:x(a, b) 调用 x.call(a, b) |
_len_(self) | 定义当被 len() 调用时的行为 |
_repr_(self) | 定义当被 repr() 调用时的行为 |
_str_(self) | 定义当被 str() 调用时的行为 |
_bytes_(self) | 定义当被 bytes() 调用时的行为 |
_hash_(self) | 定义当被 hash() 调用时的行为 |
_bool_(self) | 定义当被 bool() 调用时的行为,应该返回 True 或 False |
_format_(self, format_spec) | 定义当被 format() 调用时的行为 |
有关属性 | |
_getattr_(self, name) | 定义当用户试图获取一个不存在的属性时的行为 |
_getattribute_(self, name) | 定义当该类的属性被访问时的行为 |
_setattr_(self, name, value) | 定义当一个属性被设置时的行为 |
_delattr_(self, name) | 定义当一个属性被删除时的行为 |
_dir_(self) | 定义当 dir() 被调用时的行为 |
_get_(self, instance, owner) | 定义当描述符的值被取得时的行为 |
_set_(self, instance, value) | 定义当描述符的值被改变时的行为 |
_delete_(self, instance) | 定义当描述符的值被删除时的行为 |
比较操作符 | |
_lt_(self, other) | 定义小于号的行为:x < y 调用 x.lt(y) |
_le_(self, other) | 定义小于等于号的行为:x <= y 调用 x.le(y) |
_eq_(self, other) | 定义等于号的行为:x == y 调用 x.eq(y) |
_ne_(self, other) | 定义不等号的行为:x != y 调用 x.ne(y) |
_gt_(self, other) | 定义大于号的行为:x > y 调用 x.gt(y) |
_ge_(self, other) | 定义大于等于号的行为:x >= y 调用 x.ge(y) |
算数运算符 | |
_add_(self, other) | 定义加法的行为:+ |
_sub_(self, other) | 定义减法的行为:- |
_mul_(self, other) | 定义乘法的行为:* |
_truediv_(self, other) | 定义真除法的行为:/ |
_floordiv_(self, other) | 定义整数除法的行为:// |
_mod_(self, other) | 定义取模算法的行为:% |
_divmod_(self, other) | 定义当被 divmod() 调用时的行为 |
_pow_(self, other[, modulo]) | 定义当被 power() 调用或 ** 运算时的行为 |
_lshift_(self, other) | 定义按位左移位的行为:<< |
_rshift_(self, other) | 定义按位右移位的行为:>> |
_and_(self, other) | 定义按位与操作的行为:& |
_xor_(self, other) | 定义按位异或操作的行为:^ |
_or_(self, other) | 定义按位或操作的行为:| |
反运算 | |
_radd_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rsub_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rmul_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rtruediv_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rfloordiv_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rmod_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rdivmod_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rpow_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rlshift_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rrshift_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_rxor_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
_ror_(self, other) | (与上方相同,当左操作数不支持相应的操作时被调用) |
增量赋值运算 | |
_iadd_(self, other) | 定义赋值加法的行为:+= |
_isub_(self, other) | 定义赋值减法的行为:-= |
_imul_(self, other) | 定义赋值乘法的行为:*= |
_itruediv_(self, other) | 定义赋值真除法的行为:/= |
_ifloordiv_(self, other) | 定义赋值整数除法的行为://= |
_imod_(self, other) | 定义赋值取模算法的行为:%= |
_ipow_(self, other[, modulo]) | 定义赋值幂运算的行为:**= |
_ilshift_(self, other) | 定义赋值按位左移位的行为:<<= |
_irshift_(self, other) | 定义赋值按位右移位的行为:>>= |
_iand_(self, other) | 定义赋值按位与操作的行为:&= |
_ixor_(self, other) | 定义赋值按位异或操作的行为:^= |
_ior_(self, other) | 定义赋值按位或操作的行为:|= |
一元操作符 | |
_neg_(self) | 定义正号的行为:+x |
_pos_(self) | 定义负号的行为:-x |
_abs_(self) | 定义当被 abs() 调用时的行为 |
_invert_(self) | 定义按位求反的行为:~x |
类型转换 | |
_complex_(self) | 定义当被 complex() 调用时的行为(需要返回恰当的值) |
_int_(self) | 定义当被 int() 调用时的行为(需要返回恰当的值) |
_float_(self) | 定义当被 float() 调用时的行为(需要返回恰当的值) |
_round_(self[, n]) | 定义当被 round() 调用时的行为(需要返回恰当的值) |
_index_(self) | 1. 当对象是被应用在切片表达式中时,实现整形强制转换 2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 index 3. 如果 index 被定义,则 int 也需要被定义,且返回相同的值 |
上下文管理(with 语句) | |
_enter_(self) | 1. 定义当使用 with 语句时的初始化行为 2. enter 的返回值被 with 语句的目标或者 as 后的名字绑定 |
_exit_(self, exc_type, exc_value, traceback) | 1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么 2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作 |
容器类型 | |
_len_(self) | 定义当被 len() 调用时的行为(返回容器中元素的个数) |
_getitem_(self, key) | 定义获取容器中指定元素的行为,相当于 self[key] |
_setitem_(self, key, value) | 定义设置容器中指定元素的行为,相当于 self[key] = value |
_delitem_(self, key) | 定义删除容器中指定元素的行为,相当于 del self[key] |
_iter_(self) | 定义当迭代容器中的元素的行为 |
_reversed_(self) | 定义当被 reversed() 调用时的行为 |
_contains_(self, item) | 定义当使用成员测试运算符(in 或 not in)时的行为 |
11.2.8 self
self可以理解为自己,类似C#中的this,例:
# 定义一个类class Animal: # 方法 def __init__(self, name): self.name = name def printName(self): print('名字为:%s' % self.name)# 定义一个函数def myPrint(animal): animal.printName()dog1 = Animal('淘淘')myPrint(dog1)dog2 = Animal('贝贝')myPrint(dog2)
运行结果:
名字为:淘淘名字为:贝贝
总结
所谓的self,可以理解为自己
可以把self当做C++中类里面的this指针一样理解,就是对象自身的意思
某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可
11.2 属性
属性分为类属性和实例属性。
类属性就是属于类所有,可以直接用类名.属性名直接调用,类的属性在内存中只有一份。实例属性就是在__init__()方法中初始化的属性;
实例属性属于类的对象所有,可以用对象名.属性名的形式进行调用,但是不能用类名.属性名进行调用 。因为实例属性只有在实例创建时,才会初始化创建。
11.2.1 类属性
类属性就是类对象
所拥有的属性,它被所有类对象
的实例对象
所共有,在内存中只存在一个副本,这个和C#中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象
和实例对象
访问,例:
class People(object): name = 'Tom' #公有的类属性 __age = 12 #私有的类属性p = People()print(p.name) #正确print(People.name) #正确print(p.__age) #错误,不能在类外通过实例对象访问私有的类属性print(People.__age) #错误,不能在类外通过类对象访问私有的类属性
类属性的访问:
import timeclass Test(object): name = 'scolia'a = Test()print(Test.name) # 通过类进行访问print(a.name) # 通过实例进行访问
运行结果:
scoliascolia
可以访问的,但是,试图修改这个属性的话:
class Test(object): name = 'scolia'a = Test()Test.name = 'scolia good' # 通过类进行修改print(Test.name)print(a.name)
运行结果:
scolia goodscolia good
我们发现两者都修改成功了。再尝试通过实例来修改属性的话:
class Test(object): name = 'scolia'a = Test()a.name = 'scolia good' # 通过实例进行修改print(Test.name)print(a.name)
运行结果:
scoliascolia good
我们发现类的属性没有修改,而实例的属性则修改成功了。这究竟是为什么?
其实这里的情况非常类似于局部作用域和全局作用域。
我在函数内访问变量时,会先在函数内部查询有没有这个变量,如果没有,就到外层中找。这里的情况是我在实例中访问一个属性,但是我实例中没有,我就试图去创建我的类中寻找有没有这个属性。找到了,就有,没找到,就抛出异常。而当我试图用实例去修改一个在类中不可变的属性的时候,我实际上并没有修改,而是在我的实例中创建了这个属性。而当我再次访问这个属性的时候,我实例中有,就不用去类中寻找了。如果用一张图来表示的话:
11.2.2 实例属性(对象属性)
实例属性是在_init_(self)方法定义的属性,属于对象的本身,只能通过对象.属性来访问,不能用过类.属性来访问。例:
class People(object): address = '山东' #类属性 def __init__(self): self.name = 'xiaowang' #实例属性 self.age = 20 #实例属性p = People()p.age =12 #实例属性print(p.address) #正确print(p.name) #正确print(p.age) #正确print(People.address) #正确print(People.name) #错误print(People.age) #错误
11.2.3 私有属性
如果有一个对象,当需要对其进行修改属性时,有2种方法
- 对象名.属性名 = 数据 ---->直接修改
- 对象名.方法名() ---->间接修改
为了更好的保存属性安全,即不能随意修改,一般的处理方式为
- 将属性定义为私有属性
- 添加一个可以调用的方法,供调用
class People(object): def __init__(self, name): self.__name = name def getName(self): return self.__name def setName(self, newName): if len(newName) >= 5: self.__name = newName else: print("error:名字长度需要大于或者等于5")xiaoming = People("AI浩")print(xiaoming.__name)
Traceback (most recent call last): File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 16, in print(xiaoming.__name)AttributeError: 'People' object has no attribute '__name'
直接调用私有属性会报错,我需要定义个方法,调用方法
class People(object): def __init__(self, name): self.__name = name def getName(self): return self.__name def setName(self, newName): if len(newName) >= 5: self.__name = newName else: print("error:名字长度需要大于或者等于5")xiaoming = People("张三")xiaoming.setName("wanger")print(xiaoming.getName())xiaoming.setName("lisi")print(xiaoming.getName())
运行结果:
wangererror:名字长度需要大于或者等于5wanger
总结
- 如果需要在类外修改
类属性
,必须通过类对象
去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性
,这种方式修改的是实例属性
,不会影响到类属性
,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性
,除非删除了该实例属性
。 - Python中没有像C++中public和private这些关键字来区别公有属性和私有属性
- 它是以属性命名方式来区分,如果在属性名前面加了2个下划线’__',则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了2个下划线的话表示该方法是私有的,否则为公有的)。
11.3 继承
在程序中,继承描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫都继承自猫,而沙皮狗和斑点狗都继承足够,如下如所示:
# 定义一个父类,如下:class Cat(object): def __init__(self, name, color="白色"): self.name = name self.color = color def run(self): print("%s--在跑" % self.name)# 定义一个子类,继承Cat类如下:class Bosi(Cat): def setNewName(self, newName): self.name = newName def eat(self): print("%s--在吃" % self.name)bs = Bosi("印度猫")print('bs的名字为:%s' % bs.name)print('bs的颜色为:%s' % bs.color)bs.eat()bs.setNewName('波斯')bs.run()
运行结果:
bs的名字为:印度猫bs的颜色为:白色印度猫--在吃波斯--在跑
结论:
-
虽然子类没有定义
__init__
方法,但是父类有,所以在子类继承父类的时候这个方法就被继承了,所以只要创建Bosi的对象,就默认执行了那个继承过来的__init__
方法 -
子类在继承的时候,在定义类时,小括号()中为父类的名字
-
父类的属性、方法,会被继承给子类
11.3.1 私有属性和方法的继承
class Animal(object): def __init__(self, name='动物', color='白色'): self.__name = name self.color = color def __test(self): print(self.__name) print(self.color) def test(self): print(self.__name) print(self.color)class Dog(Animal): def dogTest1(self): # print(self.__name) #不能访问到父类的私有属性 print(self.color) def dogTest2(self): # self.__test() #不能访问父类中的私有方法 self.test()A = Animal()# print(A.__name) #程序出现异常,不能访问私有属性print(A.color)# A.__test() #程序出现异常,不能访问私有方法A.test()print("------分割线-----")D = Dog(name="小花狗", color="黄色")D.dogTest1()D.dogTest2()
运行结果:
白色动物白色------分割线-----黄色小花狗黄色
结论:
私有的属性,不能通过对象直接访问,但是可以通过方法访问私有的方法,不能通过对象直接访问私有的属性、方法,不会被子类继承,也不能被访问一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用
11.3.2 多继承
Python中多继承的格式如下:
# 定义一个父类class A: def printA(self): print('----A----')# 定义一个父类class B: def printB(self): print('----B----')# 定义一个子类,继承自A、Bclass C(A,B): def printC(self): print('----C----')obj_C = C()obj_C.printA()obj_C.printB()
运行结果:
----A--------B----
结论:
python中是可以多继承的父类中的方法、属性,子类会继承
如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用哪个?
# coding=utf-8class base(object): def test(self): print('----base test----')class A(base): def test(self): print('----A test----')# 定义一个父类class B(base): def test(self): print('----B test----')# 定义一个子类,继承自A、Bclass C(A, B): passobj_C = C()obj_C.test()print("搜索顺序:",C.__mro__) # 可以查看C类的对象搜索方法时的先后顺序
运行结果:
----A test----搜索顺序: (, , , , )
11.3.3 重写父类方法
所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法
class Cat(object): def sayHello(self): print("halou-----1")class Bosi(Cat): def sayHello(self): print("halou-----2")bosi = Bosi()bosi.sayHello()
运行结果:
halou-----2
11.3.4 调用父类的方法
调用父类的__init__方法
#coding=utf-8class Cat(object): def __init__(self,name): self.name = name self.color = 'yellow'class Bosi(Cat): def __init__(self,name): # 调用父类的__init__方法1(python2) #Cat.__init__(self,name) # 调用父类的__init__方法2 #super(Bosi,self).__init__(name) # 调用父类的__init__方法3 super().__init__(name) def getName(self): return self.namebosi = Bosi('xiaohua')print(bosi.name)print(bosi.color)
运行结果:
xiaohuayellow
调用父类的普通方法
# 父类class Dog: def bark(self): print("汪汪叫")# 子类 继承class XiaoTianQuan(Dog): def fly(self): print("我会飞") # 可以重写父类中的同名方法 def bark(self): # super().父类方法名 调用父类中的方法 (第一种方式)(推荐) super().bark() # 父类名.方法(self) 调用父类中的方法 (第二种方式,python2.x)(不推荐,父类名修改后,此处也得改) Dog.bark(self) # 注意:如果使用子类名调用方法,可能会出现递归调用 -- 死循环! # XiaoTianQuan.bark(self) # 会产生死循环 # 针对子类特有的需求,进行扩展 print("神一样的叫唤...")xtq = XiaoTianQuan()xtq.bark()
运行结果:
汪汪叫汪汪叫神一样的叫唤...
11.4 多态
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态,例:Python “鸭子类型”
class F1(object): def show(self): print ('F1.show')class S1(F1): def show(self): print ('S1.show')class S2(F1): def show(self): print ('S2.show')def Func(obj): print (obj.show())s1_obj = S1()Func(s1_obj)s2_obj = S2()Func(s2_obj)
运行结果:
S1.showNoneS2.showNone
11.5 静态方法和类方法
11.5.1 类方法
是类对象所拥有的方法,需要用修饰器@classmethod
来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls
作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以’cls’作为第一个参数的名字,就最好用’cls’了),能够通过实例对象和类对象去访问。
class People(object): country = 'china' # 类方法,用classmethod来进行修饰 @classmethod def getCountry(cls): return cls.countryp = People()print(p.getCountry()) # 可以用过实例对象引用print(People.getCountry()) # 可以通过类对象引用
类方法还有一个用途就是可以对类属性进行修改:
class People(object): country = 'china' # 类方法,用classmethod来进行修饰 @classmethod def getCountry(cls): return cls.country @classmethod def setCountry(cls, country): cls.country = countryp = People()print(p.getCountry()) # 可以用过实例对象引用print(People.getCountry()) # 可以通过类对象引用p.setCountry('japan')print(p.getCountry())print(People.getCountry())
运行结果:
chinachinajapanjapan
结果显示在用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变
11.5.2 静态方法
需要通过修饰器@staticmethod
来进行修饰,静态方法不需要多定义参数
class People(object): country = 'china' @staticmethod #静态方法 def getCountry(): return People.countryprint People.getCountry()
总结
从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用