Python基础-类变量和实例变量
Python基础-类变量和实例变量
写在前面
如非特别说明,下文均基于Python3
大纲:

1. 类变量和实例变量
在Python Tutorial中对于类变量和实例变量是这样描述的:
Generally speaking, instance variables are for data unique to each instance and class variables are for attributes and methods shared by all instances of the class:
通常来说,实例变量是对于每个实例都独有的数据,而类变量是该类所有实例共享的属性和方法。
其实我更愿意用类属性和实例属性来称呼它们,但是变量这个词已经成为程序语言的习惯称谓。一个正常的示例是:
class Dog:
kind = 'canine' # class variable shared by all instances
def __init__(self, name):
self.name = name # instance variable unique to each instance
类Dog中,类属性kind为所有实例所共享;实例属性name为每个Dog的实例独有。
2. 类对象和实例对象
2.1 类对象
Python中一切皆对象;类定义完成后,会在当前作用域中定义一个以类名为名字,指向类对象的名字。如
class Dog:
pass
会在当前作用域定义名字Dog,指向类对象Dog。
类对象支持的操作:
总的来说,类对象仅支持两个操作:
- 实例化;使用
instance_name = class_name()的方式实例化,实例化操作创建该类的实例。 - 属性引用;使用
class_name.attr_name的方式引用类属性。
2.2 实例对象
实例对象是类对象实例化的产物,实例对象仅支持一个操作:
- 属性引用;与类对象属性引用的方式相同,使用
instance_name.attr_name的方式。
按照严格的面向对象思想,所有属性都应该是实例的,类属性不应该存在。那么在Python中,由于类属性绑定就不应该存在,类定义中就只剩下函数定义了。
在Python tutorial关于类定义也这么说:
In practice, the statements inside a class definition will usually be function definitions, but other statements are allowed, and sometimes useful.
实践中,类定义中的语句通常是函数定义,但是其他语句也是允许的,有时也是有用的。
这里说的其他语句,就是指类属性的绑定语句。
3. 属性绑定
在定义类时,通常我们说的定义属性,其实是分为两个方面的:
- 类属性绑定
- 实例属性绑定
用绑定这个词更加确切;不管是类对象还是实例对象,属性都是依托对象而存在的。
我们说的属性绑定,首先需要一个可变对象,才能执行绑定操作,使用
objname.attr = attr_value
的方式,为对象objname绑定属性attr。
这分两种情况:
- 若属性
attr已经存在,绑定操作会将属性名指向新的对象; - 若不存在,则为该对象添加新的属性,后面就可以引用新增属性。
3.1 类属性绑定
Python作为动态语言,类对象和实例对象都可以在运行时绑定任意属性。因此,类属性的绑定发生在两个地方:
- 类定义时;
- 运行时任意阶段。
下面这个例子说明了类属性绑定发生的时期:
class Dog:
kind = 'canine'
Dog.country = 'China'
print(Dog.kind, ' - ', Dog.country) # output: canine - China
del Dog.kind
print(Dog.kind, ' - ', Dog.country) # AttributeError: type object 'Dog' has no attribute 'kind'
在类定义中,类属性的绑定并没有使用objname.attr = attr_value的方式,这是一个特例,其实是等同于后面使用类名绑定属性的方式。
因为是动态语言,所以可以在运行时增加属性,删除属性。
3.2 实例属性绑定
与类属性绑定相同,实例属性绑定也发生在两个地方:
- 类定义时;
- 运行时任意阶段。
示例:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
dog = Dog('Lily', 3)
dog.fur_color = 'red'
print('%s is %s years old, it has %s fur' % (dog.name, dog.age, dog.fur_color))
# Output: Lily is 3 years old, it has red fur
Python类实例有两个特殊之处:
__init__在实例化时执行Python实例调用方法时,会将实例对象作为第一个参数传递
因此,__init__方法中的self就是实例对象本身,这里是dog,语句
self.name = name
self.age = age
以及后面的语句
dog.fur_color = 'red'
为实例dog增加三个属性name, age, fur_color。
4. 属性引用
属性的引用与直接访问名字不同,不涉及到作用域。
4.1 类属性引用
类属性的引用,肯定是需要类对象的,属性分为两种:
- 数据属性
- 函数属性
数据属性引用很简单,示例:
class Dog:
kind = 'canine'
Dog.country = 'China'
print(Dog.kind, ' - ', Dog.country) # output: canine - China
通常很少有引用类函数属性的需求,示例:
class Dog:
kind = 'canine'
def tell_kind():
print(Dog.kind)
Dog.tell_kind() # Output: canine
函数tell_kind在引用kind需要使用Dog.kind而不是直接使用kind,涉及到作用域,这一点在我的另一篇文章中有介绍:Python进阶 - 命名空间与作用域
4.2 实例属性引用
使用实例对象引用属性稍微复杂一些,因为实例对象可引用类属性以及实例属性。但是实例对象引用属性时遵循以下规则:
- 总是先到实例对象中查找属性,再到类属性中查找属性;
- 属性绑定语句总是为实例对象创建新属性,属性存在时,更新属性指向的对象。
4.2.1 数据属性引用
示例1:
class Dog:
kind = 'canine'
country = 'China'
def __init__(self, name, age, country):
self.name = name
self.age = age
self.country = country
dog = Dog('Lily', 3, 'Britain')
print(dog.name, dog.age, dog.kind, dog.country)
# output: Lily 3 canine Britain
类对象Dog与实例对象dog均有属性country,按照规则,dog.country会引用到实例对象的属性;但实例对象dog没有属性kind,按照规则会引用类对象的属性。
示例2:
class Dog:
kind = 'canine'
country = 'China'
def __init__(self, name, age, country):
self.name = name
self.age = age
self.country = country
dog = Dog('Lily', 3, 'Britain')
print(dog.name, dog.age, dog.kind, dog.country) # Lily 3 canine Britain
print(dog.__dict__) # {'name': 'Lily', 'age': 3, 'country': 'Britain'}
dog.kind = 'feline'
print(dog.name, dog.age, dog.kind, dog.country) # Lily 3 feline Britain
print(dog.__dict__)
print(Dog.kind) # canine 没有改变类属性的指向
# {'name': 'Lily', 'age': 3, 'country': 'Britain', 'kind': 'feline'}
使用属性绑定语句dog.kind = 'feline',按照规则,为实例对象dog增加了属性kind,后面使用dog.kind引用到实例对象的属性。
这里不要以为会改变类属性Dog.kind的指向,实则是为实例对象新增属性,可以使用查看__dict__的方式证明这一点。
示例3,可变类属性引用:
class Dog:
tricks = []
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
d = Dog('Fido')
e = Dog('Buddy')
d.add_trick('roll over')
e.add_trick('play dead')
print(d.tricks) # ['roll over', 'play dead']
语句self.tricks.append(trick)并不是属性绑定语句,因此还是在类属性上修改可变对象。
4.2.2 方法属性引用
与数据成员不同,类函数属性在实例对象中会变成方法属性。
先看一个示例:
class MethodTest:
def inner_test(self):
print('in class')
def outer_test():
print('out of class')
mt = MethodTest()
mt.outer_test = outer_test
print(type(MethodTest.inner_test)) # <class 'function'>
print(type(mt.inner_test)) #<class 'method'>
print(type(mt.outer_test)) #<class 'function'>
可以看到,类函数属性在实例对象中变成了方法属性,但是并不是实例对象中所有的函数都是方法。
Python tutorial中这样介绍方法对象:
When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.
引用非数据属性的实例属性时,会搜索它对应的类。如果名字是一个有效的函数对象,Python会将实例对象连同函数对象打包到一个抽象的对象中并且依据这个对象创建方法对象:这就是被调用的方法对象。当使用参数列表调用方法对象时,会使用实例对象以及原有参数列表构建新的参数列表,并且使用新的参数列表调用函数对象。
那么,实例对象只有在引用方法属性时,才会将自身作为第一个参数传递;调用实例对象的普通函数,则不会。
所以可以使用如下方式直接调用方法与函数:
mt.inner_test()
mt.outer_test()
除了方法与函数的区别,其引用与数据属性都是一样的
5. 最佳实践
虽然Python作为动态语言,支持在运行时绑定属性,但是从面向对象的角度来看,还是在定义类的时候将属性确定下来。
Python基础-类变量和实例变量的更多相关文章
- 【类不类二】Python的类变量与实例变量
在研究类的时候,难免会有很多疑问,C论坛和博客园高手如云(不知道是不是也美女如云), 搜到了这篇博文,是介绍Python的类变量和实例变量的 ! 刚好在下对self.***这种形式的实例变 量不是很理 ...
- python(四)类变量和实例变量
转载自[1] 实际这是个实例变量是否指向类变量的问题. python的类变量和实例变量,顾名思义,类变量是指跟类的变量,而实例变量,指跟类的具体实例相关联的变量,具体体现为self.x 等.实际要注意 ...
- python的类变量与实例变量
python的类内部定义的变量 ,形式上没有区分实例变量和类变量(java的静态变量),测试结果如下:
- python的类变量与实例变量以及__dict__属性
关于Python的实例变量与类变量,先来看一段可能颠覆世界观的例子 #!/usr/bin/env python # -*- coding: utf_8 -*- # Date: 2016年10月10日 ...
- python中的实例方法、静态方法、类方法、类变量和实例变量
class MyTest: myname = 'peter' # add a instance attribute def __init__(self, name): self.n ...
- python的局部变量,全局变量,类变量,实例变量
定义: a.全局变量:在模块内.在所有函数外面.在class外面,这就是全局变量. b.局部变量:在函数内.在class的方法内(未加self修饰的),这就是局部变量. c. 静态变量:在class内 ...
- 关于python类变量和实例变量
今天在看python的类和实例的时候,突然发现了一个以前遗漏的点,也就是类变量和实例变量.首先需要理解一下类变量和实例变量的概念. 类全局变量:在类中定义,对类和由类生成的实例生效,如果通过方法对类变 ...
- python面向对象中类对象、实例对象、类变量、实例变量、类方法、实例方法、静态方法
1. 类对象和实例对象 Python中一切皆对象,Python类本身也是一种对象,类定义完成后,会在当前作用域中定义一个以类名为名字的命名空间.类对象具有以下两种操作: 可以通过“类名()”的方式实例 ...
- python——实例方法、静态方法、类方法、类变量和实例变量浅析
概述: 实例方法就是类的实例能够使用的方法. 静态方法是一种普通函数,就位于类定义的命名空间中,它不会对任何实例类型进行操作.使用装饰器@staticmethod定义静态方法.类对象和实例都可调用静态 ...
随机推荐
- hdu1011 Starship Troopers 树形DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 思路:很明显的树形背包 定义dp[root][m]表示以root为根,派m个士兵的最优解,那么d ...
- hdu1420 Prepared for New Acmer 简单数学
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1420 简单数学题 第一次wa在可能和会出现取模后值为负数的情况. 只要会一个数论上的简单公式(a*b) ...
- Java学习笔记——I/O流
朝辞白帝彩云间,千里江陵一日还.两岸猿声啼不尽,轻舟已过万重山. --早发白帝城 我们老师写代码有个特点,就是简洁.每一句的意图都十分明确.所以他讲课的速度也比较快. 跑题了,说说I/O流: 1.字节 ...
- jQuery之文档处理
jQuery 文档处理 1)内部插入 2)外部插入 3)包裹 4)替换 5)删除 6)复制 1.内部插入 append(content|fn) 向每个匹配的元素内部追加内容. 向所有段落中追加一些HT ...
- 使用Git上传项目代码到github
github是一个基于Git的代码托管平台,付费用户可以建私人仓库,我们一般的免费用户只能使用公共仓库,也就是代码要公开.这对于一般人来说公共仓库就已经足够了. 注册账户以及创建仓库 要想使用gi ...
- struts2.1.6教程十二、总结
本教程对struts2的基本知识进行了一些说明,关于struts2的更多详细内容应参看struts2的官方文档及提供的app实例. 下面对struts2的基本执行流程作一简要说明,此流程说明可以结合官 ...
- [原创]安全系列之端口敲门服务(Port Knocking for Ubuntu 14.04 Server)
Port Knocking for Ubuntu 14.04 Server OS:ubuntu 14.04 server 原理简单分析: 端口敲门服务,即:knockd服务.该服务通过动态的添加ipt ...
- Find modern, interactive web-based charts for R at the htmlwidgets gallery
While R's base graphics library is almost limitlessly flexible when it comes to create static graphi ...
- IBM WebSphere ESB入门指南
[TOC] 第一章 ESB介绍 本博客介绍一款ESB产品,IBM WebSphere ESB.ESB(Enterprise Service Bus)也即企业服务总线.ESB有很多产品,IBM的IBM ...
- java中的流程控制语句总结
程序的结构分类: 顺序结构:按照写代码的顺序 一次执行 选择结构:根据条件的不同有选择的执行不同的代码 循环结构:在一定条件下 反复执行某一片代码 选择结构: 也叫分支结构 根据条件的不同,有选择的执 ...