一、类和实例
1.类的定义
在Python中,定义类是通过class关键字:
class 类名(继承类): //所有类最终都会继承object类
pass
2.类的变量
定义类的变量有两种方法,静态定义和动态定义:
(1)静态定义:所谓静态定义,是指在创建类的时候定义
class Student(object):
def __init__(self, name, score): //定义初始化函数,self表示类本身(必须加),后面的变量可以任意定义
self.name = name
self.score = score
(2)动态定义:所谓动态定义,是指在实例化类之后,将变量绑定给实例
>>> bart = Student()
>>> bart.name = 'Bart Simpson'
>>> bart.name
'Bart Simpson'
3.类的方法
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self): //定义类的方法,除了第一个参数是self外,其他和普通函数一样
print('%s: %s' % (self.name, self.score))
4.类的实例化
将类实例化之后,可以调用类的变量和方法:
>>> 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) //实例化类,调用类的__init__方法,self不需要传入
>>> bart.name //调用类的变量
'Bart Simpson'
>>> bart.print_score() //调用类的方法
Bart Simpson:59
二、访问限制
参考类参数的动态定义,我们可以从外部改变一个实例的参数。如果要让内部不被外部访问,可以把属性的名称前加上两个下划__,让参数变成私有变量(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))
...
>>>
>>>
>>> bart = Student('Bart Simpson', 59)
>>> bart.__name //外部不能直接访问类的__name变量
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
>>>
>>>
>>> bart.__name = 'Bob' //给__name设置参数
>>> bart.__name //表面上定义成功,实际上是给实例定义了另一个变量
'Bob'
>>> bart.print_score() //可见结果并不成功
Bart Simpson:59
实际上,外部想要访问类内部的私有变量是有方法的(只是不推荐这么做):
>>> bart._Student__name //Python解释器对外把__name变量改成了_Student__name,但是不同的解释器定义不同,所有不推荐这么做
'Bart Simpson'
三、继承和多态
1.继承:当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。实例如下:
>>> class Animal(object): //定义一个Animal作为基类
... def run(self): //定义一个run()方法
... print('Animal is running...')
...
>>> class Dog(Animal): //定义一个子类Dog,继承Animal基类
... pass
...
>>> class Cat(Animal): //定义一个子类Cat,继承Animal基类
... pass
...
>>> dog = Dog()
>>> dog.run() //dog实例调用了基类的run()方法
Animal is running...
>>> cat = Cat()
>>> cat.run() //Cat实例调用了基类的run()方法
Animal is running...
子类Cat和Dog继承了基类Animal的run()方法,理论上子类将继承基类的所有方法和参数。
如果子类要对基类的方法进行改写,也可以在子类中重新定义该方法:
>>> class Dog(Animal):
... def run(self): //重新定义run()方法
... print('Dog is running...')
... def eat(self): //也可以自定义其他方法
... print('Eating meat...')
...
>>> dog = Dog()
>>> dog.run() //dog实例调用重新定义的run()方法
Dog is running...
>>> dog.eat()
Eating meat...
2.多态:在讲解什么是多态前,可以先通过isinstance(实例,类型)判断子类和基类的关系
>>> animal = Animal()
>>> dog = Dog()
>>> isinstance(animal, Animal) //animal的数据类型是Animal
True
>>> isinstance(dog, Dog) //dog的数据类型是Dog
True
>>> isinstance(dog, Animal) //dog的数据类型可以是Animal
True
>>> isinstance(animal, Dog) //而animal的数据类型却不可以是Dog
False
从上面的例子可以看出,子类的数据类型可以用父类的数据类型来表示,而父类的数据类型却不可以用子类的数据类型来表示。利用上面的特性,我们就可以获得多态的便利性:
>>> def run_twice(animal): //定义一个函数
... animal.run()
... animal.run()
...
>>> run_twice(Animal()) //传入Animal类的实例,则调用Animal类的run()方法
Animal is running...
Animal is running...
>>> run_twice(Dog()) //而传入Dog类的实例,则调用Dog类的run()方法
Dog is running...
Dog is running...
>>> run_twice(Cat()) //以此类推,调用不同的子类,不需要对run_twice()方法做任何修改。实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
Cat is running...
Cat is running...
实际上,Python这种动态语言,和静态语言(Java等)相比,还有一个更大的好处就是:
语言 | 好处 |
---|---|
静态语言(Java等) | 如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。 |
动态语言(Python等) | 动态语言则不一定需要传入Animal类型,只需要保证传入的对象有一个run()方法,这就是所谓的“鸭子类型”。 |
四、获取对象信息
问:当我们获得一个对象的引用时,如何知道这个对象是什么类型,有哪些方法呢?
答:可以使用type()、isinstance()、dir()。三个函数的用法如下所示:
函数 | 用法 |
---|---|
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'> |
|
在if语句中判断两个变量的type类型: type(123) == type(456) -> True type(123) == int -> True type('abc') == type('123') -> True type('abc') == str -> True type('abc') == type(123) -> False |
|
在if语句中判断变量是否是函数:(使用types模块中定义的常量) type(fn) == types.FunctionType -> True type(abs) == types.BuiltinFunctionType -> True type(lambda x: x) == types.LambdaType -> True type((x for x in range(10))) == types.GeneratorType -> True |
|
isinstance() | 首先有以下继承关系:object -> Animal -> Dog -> Husky 它们的实例分别是: animal dog husky 判断它们的类型: isinstance(animal, Animal) -> True isinstance(dog, Animal) -> True isinstance(husky, Animal) -> True isinstance(husky, 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 |
|
dir() | dir()函数返回一个包含字符串的list,比如获得一个str对象的所有属性和方法: >>> dir('ABC') ['__add__', '__class__', ..., '__subclasshook__', 'capitalize', 'casefold', ..., 'zfill'] |
仅仅把属性和方法列出来是不不够的,配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态: ---------------------------------------定义一个类并实例化它的对象--------------------------------- >>> class MyObject(object): ... def __init__(self): ... self.x = 9 ... def power(self): ... return self.x * self.x ... >>> obj = MyObject() ---------------------------------------判断该对象是否包含某属性或方法--------------------------- hasattr(obj, 'x') -> True hasattr(obj, 'y') -> False hasattr(obj,'power') -> True ---------------------------------------获取该对象的某属性或方法------------------------------------ getattr(obj, 'x') -> 9 getattr(obj, 'z', 404) -> 404 //获取属性'z',如果不存在,返回默认值404 fn = getattr(obj, 'power') //获取obj的power方法 ---------------------------------------设置该对象的某属性------------------------------------ setattr(obj, 'y', 19) |
五、实例属性和类属性
实例属性和类属性的定义如下:
属性 | 定义 |
---|---|
类属性 | 所谓类属性,是指我们在定义类的时候,在类内部定义好的属性 |
实例属性 | 而实例属性是指,将一个类实例化之后,在运行状态下给实例定义的属性 |
例子如下:
>>> class Student(object):
... def __init__(self, name):
... self.name = name //name在类内定义,表示类属性
...
>>> s = Student('Bob') //将Student类实例化
>>> s.name
'Bob'
>>> s.score = 97 //给s实例定义一个score属性,score表示实例属性
在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。