python之类与对象进阶(一)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


1. 对于self的理解

self 参数的具体作用是什么呢?打个比方,如果把类比作造房子的图纸,那么类实例化后的对象是真正可以住的房子。根据一张图纸(类),我们可以设计出成千上万的房子(类对象),每个房子长相都是类似的(都有相同的类变量和类方法),但它们都有各自的主人,那么如何对它们进行区分呢?

当然是通过 self 参数,它就相当于每个房子的门钥匙,可以保证每个房子的主人仅能进入自己的房子(每个类对象只能调用自己的类变量和类方法)。

其实 Python 类方法中的 self 参数就相当于 C++ 中的 this 指针。

2. 类变量与实例变量

  • 类体中、所有函数之外:此范围定义的变量,称为类属性或类变量,类似C++中类的static变量;
  • 类体中,所有函数内部:以“self.变量名”的方式定义的变量,称为实例属性或实例变量
不仅如此,类方法也可细分为实例方法、静态方法和类方法,后续会做详细介绍。

类变量:

class CLanguage :
    # 下面定义了2个类变量
    name = "C语言中文网"
    add = "http://c.biancheng.net"
    # 下面定义了一个say实例方法
    def say(self, content):
        print(content)

name、add就是类变量。
类变量的特点是:所有类的实例化对象都同时共享类变量,也就是说,类变量在所有实例化对象中是作为公用资源存在的。类方法的调用方式有 2 种,既可以使用类名直接调用,也可以使用类的实例化对象调用(但不推荐使用实例化对象调用)。

#使用类名直接调用
print(CLanguage.name)   
print(CLanguage.add)
#修改类变量的值
CLanguage.name = "Python教程"
CLanguage.add = "http://c.biancheng.net/python"
print(CLanguage.name)
print(CLanguage.add)

程序运行结果为:

C语言中文网
http://c.biancheng.net
Python教程
http://c.biancheng.net/python

可以看到,通过类名不仅可以调用类变量,也可以修改它的值。并且,具有“牵一发动全身”的效果,也就是说,比如用这个类实例化了好多个对象,如果按照类名调用的方式修改了类变量,则所有的对象的类变量都会改变。

当然,也可以使用类对象来调用所属类中的类变量(此方式不推荐使用,原因见后续)。例如:

clang = CLanguage()
print(clang.name)
print(clang.add)

除了可以通过类名访问类变量之外,还可以动态地为类和对象添加类变量(只可以用类名添加类变量,而不能用对象添加类变量,原因见后续)。例如,在 CLanguage 类的基础上,添加以下代码:

clang = CLanguage()
CLanguage.catalog = 13
print(clang.catalog)   #输出为13

实例变量:
实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量,其特点是只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问。

class CLanguage :
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    # 下面定义了一个say实例方法
    def say(self):
        self.catalog = 13

name、add 以及 catalog 都是实例变量。其中,由于 init() 函数在创建类对象时会自动调用,而 say() 方法需要类对象手动调用。因此,CLanguage 类的类对象都会包含 name 和 add 实例变量,而只有调用了 say() 方法的类对象,才包含 catalog 实例变量。
如:

clang = CLanguage()
print(clang.name)
print(clang.add)
#由于 clang 对象未调用 say() 方法,因此其没有 catalog 变量,下面这行代码会报错
#print(clang.catalog)
clang2 = CLanguage()
print(clang2.name)
print(clang2.add)
#只有调用 say(),才会拥有 catalog 实例变量
clang2.say()
print(clang2.catalog)

前面讲过,通过类对象可以访问类变量,但无法修改类变量的值。这是因为,通过类对象修改类变量的值,不是在给“类变量赋值”,而是定义新的实例变量。 例如,如下程序:

class CLanguage :
    name_static = "Python"
    add_static = "http"
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    # 下面定义了一个say实例方法
    def say(self):
        self.catalog = 13

clang = CLanguage()
#clang访问类变量
print(clang.name_static)
print(clang.add_static)
clang.name_static = "Python教程"
clang.add_static = "http://c.biancheng.net/python"
#clang实例变量的值
print(clang.name)
print(clang.add)
#类变量的值
print(CLanguage.name_static)   #并没有任何改变
print(CLanguage.add_static)    #并没有任何改变
#新增的实例变量的值
print(clang.name_static)   
print(clang.add_static)

则输出为:

Python
http
C语言中文网
http://c.biancheng.net
Python
http
Python教程
http://c.biancheng.net/python
  • 类中,实例变量和类变量可以同名,但这种情况下使用类对象将无法调用类变量,它会首选实例变量,这也是不推荐“类变量使用对象名调用”的原因。
  • 另外,和类变量不同,通过某个对象修改实例变量的值,不会影响类的其它实例化对象,更不会影响同名的类变量。
  • 可以动态的为特定的对象添加实例变量。如clang.money = 30。

3. 类方法、静态方法与实例方法

和类属性一样,类方法也可以进行更细致的划分,具体可分为类方法、实例方法和静态方法。
区分这 3 种类方法是非常简单的,即采用 @classmethod 修饰的方法为类方法;采用 @staticmethod修饰的方法为静态方法;不用任何修改的方法为实例方法。


实例化方法

实例方法最大的特点就是,它最少也要包含一个 self 参数,用于绑定调用此方法的实例对象(Python 会自动完成绑定)。实例方法通常会用类对象直接调用,例如:

clang = CLanguage()
clang.say()

python也支持仅用类名来调用实例方法,但此方式需要手动给 self 参数传值。如:

#类名调用实例方法,需手动给 self 参数传值
clang = CLanguage()
CLanguage.say(clang)

类方法

Python 类方法和实例方法相似,它最少也要包含一个参数,只不过类方法中通常将其命名为 clsPython 会自动将类本身绑定给 cls 参数(注意,绑定的不是类对象),类方法中只能访问到类变量,访问不到实例变量。(有点像c++中的static函数)。

和实例方法最大的不同在于,类方法需要使用@classmethod修饰符进行修饰,例如:

class CLanguage:
    name = "python"
    #类构造方法,也属于实例方法
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    #下面定义了一个类方法
    @classmethod
    def info(cls):
        print("正在调用类方法",cls)
        print("类变量name = ",cls.name)

注意,如果没有 @classmethod,则 Python 解释器会将 info() 方法认定为实例方法,而不是类方法。
类方法推荐使用类名直接调用,当然也可以使用实例对象来调用(不推荐)。例如,在上面 CLanguage 类的基础上,在该类外部添加如下代码:

#使用类名直接调用类方法
CLanguage.info()
#使用类对象调用类方法
clang = CLanguage()
clang.info()

输出为:

正在调用类方法 <class '__main__.CLanguage'>
类变量name =  python
正在调用类方法 <class '__main__.CLanguage'>
类变量name =  python

一个关于类方法的具体实例:

class ColorTest(object):
    color = "color"
    @classmethod
    def value(self):
        return self.color

class Red(ColorTest):
    color = "red"

class Green(ColorTest):
    color = "green"

g = Green()
print(g.value())
print(Green.value())

输出为:

green
green

静态方法

静态方法没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。也正因为如此,类的静态方法中无法调用任何类属性和类方法。(感觉这种函数没啥意义)
静态方法需要使用@staticmethod修饰,例如:

class CLanguage:
    @staticmethod
    def info(name,add):
        print(name,add)

静态方法的调用,既可以使用类名,也可以使用类对象,例如:

#使用类名直接调用静态方法
CLanguage.info("C语言中文网","http://c.biancheng.net")
#使用类对象调用静态方法
clang = CLanguage()
clang.info("Python教程","http://c.biancheng.net/python")

输出为:

C语言中文网 http://c.biancheng.net
Python教程 http://c.biancheng.net/python

一个关于静态方法的具体实例:

import time
class TimeTest(object):
    def __init__(self,hour,minute,second):
        self.hour = hour
        self.minute = minute
        self.second = second
    @staticmethod   
    def showTime():      
        return time.strftime("%H:%M:%S", time.localtime())
 
     
print TimeTest.showTime()   
t = TimeTest(2,10,10)
nowTime = t.showTime()
print nowTime

如上,使用静态函数,既可以将获得时间的函数功能与实例解绑,我想获得当前时间的字符串时,并不一定需要实例化对象,此时更像是一种名称空间(类似c++中的namespace)。
我们可以在类外面写一个简单的方法来做这些,但是这样做就扩散了类代码的关系到类定义的外面,这样写就会导致以后代码维护的困难。

在实际编程中,几乎不会用到类方法和静态方法,因为我们完全可以使用函数代替它们实现想要的功能,但在一些特殊的场景中(例如工厂模式中),使用类方法和静态方法也是很不错的选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值