1.类和实例:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'
bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
print('bart.name =', bart.name)
print('bart.score =', bart.score)
bart.print_score()
print('grade of Bart:', bart.get_grade())
print('grade of Lisa:', lisa.get_grade())
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self
,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
2.访问限制:
在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。但是,外部代码还是可以自由地修改一个实例的name
、score
属性。
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__
,在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name
和实例变量.__score
了:
>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
如果外部代码要获取name和score,可以给Student类增加get_name
和get_score
这样的方法。在方法中,可以对参数做检查,避免传入无效的参数。
需要注意的是,在Python中,变量名类似__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__
、__score__
这样的变量名。
双下划线开头的实例变量不能直接访问__name
是因为Python解释器对外把__name
变量改成了_Student__name
,所以,仍然可以通过_Student__name
来访问__name
变量。强烈不建议这么写!
练习:
class Student(object):
def __init__(self,name,gender):
self.name = name
self.__gender = gender
def get_gender(self):
return self.__gender
def set_gender(self,new_gender):
self.__gender =new_gender
return self.__gender
def print_gender(self):
print(self._gender)
bart = Student('Bart', 'male')
if bart.get_gender() != 'male':
print('测试失败!')
else:
bart.set_gender('female')
if bart.get_gender() != 'female':
print('测试失败!')
else:
print('测试成功!')
bart.__gender='hhh' #注意,这种情况实际上是新增了一个bart.__gender变量
print(bart.get_gender())
print(bart.__gender)
3.继承和多态:简而言之,一个子类a继承了父类b,a类就拥有了父类所有的属性和方法
详细情况略
4.获取对象信息:
- 判断对象类型,使用
type()
函数:
type(123)
<class 'int'>
- 判断一个变量是否是某个类型可以用
isinstance()
判断:
a = list() # a是list类型
b = Animal() # b是Animal类型
c = Dog() # c是Dog类型
>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True
在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。即c即时Dog类也是Animal类。
并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True
- 如果要获得一个对象的所有属性和方法,可以使用
dir()
函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:>>> dir('ABC') ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
类似
__xxx__
的属性和方法在Python中都是有特殊用途的,比如__len__
方法返回长度。在Python中,如果你调用len()
函数试图获取一个对象的长度,实际上,在len()
函数内部,它自动去调用该对象的__len__()
方法,所以,下面的代码是等价的:>>> len('ABC') 3 >>> 'ABC'.__len__() 3
我们可以直接操作一个对象的状态:
class MyObject(object): def __init__(self): self.x = 9 def power(self): return self.x * self.x obj = MyObject()
紧接着,可以测试该对象的属性:
>>> hasattr(obj, 'x') # 有属性'x'吗? True >>> obj.x 9 >>> hasattr(obj, 'y') # 有属性'y'吗? False >>> setattr(obj, 'y', 19) # 设置一个属性'y' >>> hasattr(obj, 'y') # 有属性'y'吗? True >>> getattr(obj, 'y') # 获取属性'y' 19 >>> obj.y # 获取属性'y' 19
如果试图获取不存在的属性,会抛出AttributeError的错误,可以传入一个default参数,如果属性不存在,就返回默认值:
>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404 404
5.实例属性和类属性
class Student(object): name = 'Student'
这种属性是类属性,归Student类所有,当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到。来测试一下:
>>> class Student(object): ... name = 'Student' ... >>> s = Student() # 创建实例s >>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性 Student >>> print(Student.name) # 打印类的name属性 Student >>> s.name = 'Michael' # 给实例绑定name属性 >>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性 Michael >>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问 Student >>> del s.name # 如果删除实例的name属性 >>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了 Student
从上面的例子可以看出,在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。