Python中的类与实例对象
类和对象实例
类是图纸,实例是成品,实例是照着图纸生产的,图纸设计的特征,成品都有。写代码本质上是画图纸,实例就是一个个按图纸生产好的零部件为我们所用。
基础
定义类的方式
class Student(object):
pass
创建实例的方式
>>> bart = Student()
>>> bart
<__main__.Student object at 0x10a67a590>
>>> Student
<class '__main__.Student'>
__init__方法
在创建类的时候绑定属性,self
表示创建的实例本身
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
封装性
访问类的数据,没必要在通过外部函数去取数据,将固定的数据存取直接在类里面封装成方法,访问时调用实例的方法就行了
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))
访问限制
类的某些属性不想被外部访问,声明时在变量前加__表示私有变量
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))
>>> 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'
当然如果你还是想访问,可以按照我的规则来,我给你提供set和get函数给你存取实例,防止你乱改乱访问。
*不要给一个私有属性赋值,python会给你创建一个相应的不一样的属性
>>> bart = Student('Bart Simpson', 59)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # 设置__name变量!
>>> bart.__name
'New Name'
继承和多态
面向对象编程,都要讨论继承与多态的属性。
继承
类分父类和子类,子类继承父类,子类拥有父类的所有属性,并且在此之上添加自己独有的属性,就比如汽车的图纸,父类的图纸只有汽车的框架和用到的基础零件,至于子类就可以在此之上发扬自己的特色。越野,面包,超跑,你自己二次设计。
简单的继承写法
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
dog = Dog()
dog.run()
cat = Cat()
cat.run()
#result
Animal is running...
Animal is running...
覆盖父类的方法
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
#result
Dog is running...
Cat is running...
多态
多态是个字面意思上不太好理解的词。
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
定义一个class实际上就是定义了类似于 list,str一样的数据类型,用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也是Animal类型的
>>> isinstance(c, Animal)
True
用法
def run_twice(animal):
animal.run()
animal.run()
>>> run_twice(Animal())
Animal is running...
Animal is running...
>>> run_twice(Dog())
Dog is running...
Dog is running...
新增一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
“开闭”原则:
对扩展开放:允许新增Animal子类;
对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
鸭子类型
python不严格遵守继承,传入的对象有相应的方法就可以运行。
获取对象数据
type()
可以用type()来判断基本类型,函数和类:
>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>
>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>
isinstance()
对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。
object -> Animal -> Dog -> Husky
>>> a = Animal()
>>> d = Dog()
>>> h = Husky()
>>> isinstance(h, Husky)
True
>>> isinstance(h, Dog)
True
>>> isinstance(h, Animal)
True
>>> isinstance(d, Dog) and isinstance(d, Animal)
True
>>> isinstance(d, Husky)
False
# 能用type()判断的基本类型也可以用isinstance()判断
>>> 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
类属性与实例属性
>>> 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