类是创建实例的模板,而实例则是一个一个具体的对象,各个对象拥有的数据都互相独立,互不影响;
Python是动态语言,即边解释边执行。和静态语言不同,Python允许对实例变量绑定任何数据。也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称可能不同。
说明:变量==属性,对象==示例
class Student(object):
pass
p1 = Student()
p2 = Student()
p1.age = 18 # 在类外为对象p1动态绑定属性age
p2.sex = 'M' # 在类外为对象p2动态绑定属性sex
print(p1.age) # 18
print(p2.sex) # M
print(p1.sex) # 程序崩溃。对象p1并没有属性sex
print(p2.age) # 程序崩溃。对象p2并没有属性age
一、类中的变量类型:类变量,对象变量 和 方法局部变量
python类中变量分为三种:类变量,对象变量和方法局部变量。
- 类变量:为类绑定的变量,所有对象共享(静态变量)。采用ClassName.obj访问和修改
- 对象变量:为各个对象绑定的变量。属于各个对象所有,互不干扰。
- 方法局部变量:类中方法的局部变量。
1.1 变量定义和调用示例
class Student(object):
count = 60 # count是类变量
def __init__(self, name_local, age_local):
# name_local, age_local 是方法局部变量
# , self.age 是对象变量
= name_local
self.age = age_local
p1 = Student("bacon", 23)
p2 = Student("sr", 24)
# 1. 类变量调用
Student.count = 80 # 采用Classname.obj修改类变量
print(Student.count) # 80,采用Classname.obj访问类变量
print(p1.count) # 80,采用instance.obj访问类变量
print(p2.count) # 80
# 2. 对象变量
print(, p1.age) # bacon 23
print(, p2.age) # sr 24
p1.sex = "M" # 为p1绑定新的变量sex
p2.bir = "1995.02" # 为p2绑定新的变量bir
print(p1.sex) # M
print(p1.bir) # 程序崩溃,p1并没有属性bir
print(p2.sex) # 程序崩溃,p2并没有属性sex
print(p2.bir) # 1995.02
1.2 类变量与对象变量冲突问题
在编写程序的时候,不要对对象变量和类变量使用相同的名字。因为相同名称的对象变量将屏蔽掉类变量。
class Student(object):
name = 'Student' # 类变量
s = Student()
print() # Student, 对象s没有name属性,会继续查找类的name属性
print() # Student, 打印类的name属性
= 'Michael' # 给对象s绑定对象变量name(注意还有一个类变量name,只是重名了)
print() # Michael,由于对象变量优先级比类变量高,因此,它会屏蔽掉类的name变量
print() # Student, 但是类变量并未消失,用仍然可以访问
del # 如果删除实例的name属性
print() # Student, 再次调用,由于对象的name变量没有找到,就显示类的name变量
二、类函数和变量的属性:公有 和 私有
2.1 类函数的属性
- 类函数与一般函数区别在于,类的函数必须包含self参数,且为第一个参数,表示类的实例对象。
- 类的私有函数:函数命名为 __func(self),以两个下划线开头,不能在类的外部掉用。
class Student(object):
def func(self):
print("public function")
def __pfun(self):
print("private function")
def call(self):
self.__pfun()
obj = Student()
obj.func() # public function
obj.__pfunc() # 私有函数不能再类外调用,程序崩溃
obj.call() # private function
2.2 类变量的属性
- 类的私有变量同样以双下划线开头进行命名。
- 双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问
__name
是因为Python解释器对外把__name
变量改成了_ClassName__name
,所以,仍然可以通过_ClassName__name
来访问__name
变量。强烈建议不要这么干,因为不同版本的Python解释器可能会把__name
改成不同的变量名。- 经常看到以一个下划线开头的对象变量名,比如
_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
class MyClass(object):
num = 1
__pnum = 2
print(MyClass.num) # 1
print(MyClass.__pnum) # 程序崩溃,私有变量不能在类外调用
print(MyClass._MyClass__pnum) # 2
注意下面的错误写法:
class Student(object):
__name = "student"
s = Student()
s.__name = "new name"
print(s.__name) # new name
'''
表面上看,外部代码“成功”地设置了__name变量,
但实际上这个__name变量和class内部的__name变量不是一个变量!
内部的__name变量已经被Python解释器自动改成了_Student__name,
而外部代码给对象s新增了一个__name变量。
'''
三、获取对象信息
- 判断class的类型,可以使用
isinstance()
函数。- 能用
type()
判断的基本类型也可以用isinstance()
判断.- 使用
dir()
函数来获取一个对象的所有属性和方法。配合getattr()
、setattr()
以及hasattr()
,我们可以直接操作一个对象的状态
3.1 使用isinstance()函数判断变量类型
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
def run(self):
print('Dog is running...')
a = Animal()
d = Dog()
print(isinstance(a, Animal)) # True
print(isinstance(d, Animal)) # True
print(isinstance(d, Dog)) # True
isinstance('a', str) # True
isinstance(123, int) # True
isinstance(b'a', bytes) # True
判断一个变量是否是某些类型中的一种
isinstance([1, 2, 3], (list, tuple)) # True
isinstance((1, 2, 3), (list, tuple)) # True
3.2 使用dir()获取一个对象的所有属性和方法
print(dir("abc"))
'''
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__',
'__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__',
'__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode',
'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha',
'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace',
'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition',
'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split',
'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
'''
3.3 getattr()
、setattr()和
hasattr()
class MyObject(object):
def __init__(self):
self.x = 9
def power(self):
return self.x * self.x
obj = MyObject()
# hasattr()方法
print(hasattr(obj, 'x')) # True
print(obj.x) # 9
# setattr()方法
print(hasattr(obj, 'y')) # False
setattr(obj, 'y', 19)
# getattr()方法
print(getattr(obj, 'y', 404)) # 19
print(getattr(obj, 'z', 404)) # 404
四、类的动态属性
Python内置的
@property
装饰器就是负责把一个方法变成属性调用。@property
广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。
4.1 场景引入
class Student(object):
score = 60
s = Student()
s.score = 9999 # 明显分数不合理,希望在赋值时进行逻辑检测
4.2 常规方法
为了限制score的范围,可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数.
class Student(object):
def get_score(self):
return self._score
def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s = Student()
s.set_score(60)
print(s.get_score()) # 60
s.set_score(9999) # 程序报错
4.3 使用@property
装饰器方法
上面的调用方法又略显复杂,没有直接用属性这么直接简单。Python内置的@property
装饰器就是负责把一个方法变成属性调用的!
把一个getter方法变成属性,只需要加上@property
就可以了,此时,@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter方法变成属性赋值
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s = Student()
s.score = 60
print(s.score)
s.score = 9999 # 程序报错
五、类的特殊变量
- 在Python中,变量名类似
__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量。
5.1 __slots__
如果我们想要限制实例的属性,比如,只允许对Student实例添加
name
和age
属性。为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性
__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student() # 创建新的实例
s.score = 99 # 程序崩溃。绑定属性'score',由于'score'没有被放到__slots__中,所以不能绑定score属性
5.2 __len__()
为了能让class作用于
len()
函数。如 len(list)
5.3 __str__()
print(obj):输出实例时显示的内容。
class Student(object):
def __init__(self, name):
= name
def __str__(self):
return 'Student object (name: %s)' %
print(Student('Michael')) # Student object (name: Michael)
5.4 __repr__()
直接显示变量调用的不是
__str__()
,而是__repr__()。
class Student(object):
def __init__(self, name):
= name
def __str__(self):
return 'Student object (name=%s)' %
__repr__ = __str__
5.5 __iter__()
如果一个类想被用于
for ... in
循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误时退出循环。
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100000: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
5.6 __getitem__()
实现像list那样按照下标取出元素
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
print(Fib()[0]) # 1
切片形式
class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
print(Fib()[0:5]) # [1, 1, 2, 3, 5]
5.7 __getattr__
当调用不存在的属性时,Python解释器会试图调用
__getattr__(self, 'name')
来尝试获得属性,这样,我们就有机会返回name
的值.只有在没有找到属性的情况下,才调用
__getattr__
,已有的属性,比如name
,不会在__getattr__
中查找。
class Student(object):
def __init__(self):
= 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
s = Student()
print(s.score) # 99
5.8 __call__
一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用
instance.method()
来调用。能不能直接在实例本身上调用呢?在Python中,答案是肯定的。任何类,只需要定义一个__call__()
方法,就可以直接对实例进行调用。
class Student(object):
def __init__(self, name):
= name
def __call__(self):
print('My name is %s.' % )
s = Student('Michael')
print(s()) # My name is Michael.
六、枚举类
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
'''
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12
'''
参考