一、面向对象技术简介
1、面向对象简介
面向对象编程(Object Oriented Programing,OOP)是一种编程思想,OOP把对象当成程序的一个基本单元,一个对象包含数据和操作数据的方法。面向对象编程的三大特性如下:
A、封装,可以隐藏实现细节,使代码模块化。
B、继承,可以通过扩展已存在的类来实现代码重用,避免重复编写相同的代码。
C、多态,封装和继承的目的都是为了实现代码重用, 而多态是为了实现接口重用,使得类在继承和派生的时候能够保证任何一个类的实例都能正确调用约定好的属性和方法。
面向对象编程通过封装、继承、多态实现了软件工程的重用性、灵活性、扩展性三个目标。
2、面向对象术语
类(Class)是用来描述具有相同的属性和方法的对象的集合,定义了集合中每个对象所共有的属性和方法。
对象是类的实例,Python中对象包括两个数据成员(类变量和实例变量)和方法。
方法是类中定义的函数。
类变量在类的所有实例化对象中是公用的。类变量定义在类中且在函数体外。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行重写(override)。
继承是一个派生类(derived class)继承基类(base class)的字段和方法。继承允许把一个派生类的对象作为一个基类对象对待。
实例化:创建一个类的实例,类的具体对象。
3、对象的属性
在python当中一切皆对象,每个对象都有三个属性:id、类型type和数值。id是对象的地址,id相同则必为同一对象,不同对象的值可以相同。
在学习过程中有什么不懂得可以加我的
python学习交流扣扣qun,784-758-214
群里有不错的学习视频教程、开发工具与电子书籍。
与你分享python企业当下人才需求及怎么从零基础学习好python,和学习什么内容
# -*- coding:utf-8 -*-
x = 10
print(id(x))
print(type(x)) # <class 'int'>
print(x)
y = 10
print(id(y))
print(type(y)) # <class 'int'>
print(y)
print(x is y) # True
二、类的定义
1、类的定义
类是一种抽象数据类型,是对现实世界的一类数据及其操作的封装。
类定义语法格式如下:
class ClassName:
<statement-1>
.
.
.
<statement-N>
类实例化后,可以使用其属性,创建一个类后,可以通过类名访问其类属性。
Person类有以下3个属性:
nationality:国籍
name:姓名
id:×××号码
import uuid
class Person:
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
所有人的国籍基本都是相同的,且允许直接通过类或实例来访问,允许随意修改。
大部分人的姓名是不同的,且允许直接通过类的实例来访问和随意修改。
所有人的×××号码都是不一样的,且不允许直接通过类或实例来访问或随意修改。
2、类的实例化
import Person
bauer = Person.Person("Bauer")
bauer.hello()
3、类成员的可见性
Python中默认所有的成员都是公有成员,但私有成员是以两个下划线开头的名字表示私有成员,私有成员不允许直接访问,只能通过内部方法进行访问,私有成员也不允许被继承。
Python中通过在类变量、实例变量、类方法、实例方法前加__
前缀,可以将其对外进行隐藏,变为类的私有变量或函数。由于Python中内置变量或函数使用__
前后缀,因此,不推荐私有的变量或函数加__
前后缀,只加__
前缀。
Python作为动态语言,允许类或实例动态增加属性,与类内部的私有的属性并不相同。
Python类维护了一个用于保存类的数据的字典,字典内部Python将私有成员改名为_ClassName + __variable_name
,因此在类外通过访问私有变量新的名称可以访问相应的私有变量。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
if __name__ == "__main__":
bauer = Person("Bauer")
print(bauer.__dict__)
print(bauer._Person__id)
"""
output:
{'name': 'Bauer', '_Person__id': 'ed496846-94c7-11e9-80c4-5ce0c5e8bcf0'}
ed496846-94c7-11e9-80c4-5ce0c5e8bcf0
"""
三、类的属性
1、类属性
直接定义在class下的属性是公有属性/类属性,类属性是类的所有实例对象共同所有的,因此默认情况下类属性值只会保留一份,而不会为类的每个实例都保存一份。
类属性可以使用ClassName.VariableName访问,在实例方法内部也可以使用self.__class__.VariableName
进行访问。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
def sayHello(self):
print("Hello,I come from %s" % self.__class__.nationality)
if __name__ == "__main__":
bauer = Person("Bauer")
bauer.sayHello()
jack = Person("Jack")
print(Person.nationality, bauer.nationality, jack.nationality)
bauer.nationality = "USA"
print(Person.nationality, bauer.nationality, jack.nationality)
Person.nationality = "Germany"
print(Person.nationality, bauer.nationality, jack.nationality)
"""
output:
Hello,I come from China
China China China
China USA China
Germany USA Germany
"""
类属性可以通过类直接访问,也可以直接通过实例进行访问; 如果通过类的某个实例对类属性进行修改,本质上是为该实例添加了一个与类属性名称相同的实例属性,对真正的类属性没有影响,因此不会影响其它实例获取类属性的值; 通过类对类属性进行修改,必然会改变类属性的值,对类的所有实例是都有影响的。
2、实例属性
实例属性又称成员属性或成员变量,是类的每个实例对象单独持有的属性。实例属性必须在类的init方法中进行声明。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
if __name__ == "__main__":
bauer = Person("Bauer")
jack = Person("Jack")
print(bauer.name, jack.name)
bauer.name = "Jack Bauer"
jack.name = "Chen Jack"
print(bauer.name, jack.name)
#print(Person.name) ## AttributeError: type object 'Person' has no attribute 'name'
"""
output:
Bauer Jack
Jack Bauer Chen Jack
"""
通过类访问成员属性会报错:
print(Person.name)
实例属性可以直接通过实例对象来访问和更改,是每个实例对象独有的,某个实例对象的实例属性被更改不会影响其它实例对象的相同属性的值。实例属性的值不能通过类来访问和修改。
Python作为动态语言,可以在类外部动态增加实例对象的属性。
3、私有属性
私有属性和实例属性必须在__init__
方法中进行声明,但私有属性的属性名需要以双下划线__
开头,比如Person中的__id
属性。私有属性是一种特殊的实例属性,只允许在实例对象的内部(成员方法或私有方法中)访问,而不允许在实例对象的外部通过实例对象或类来直接访问,也不能被子类继承。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
if __name__ == "__main__":
bauer = Person("Bauer")
bauer.hello()
# print(bauer.__id) # AttributeError: 'Person' object has no attribute '__id'
# print(Person.__id) # AttributeError: type object 'Person' has no attribute '__id'
"""
output:
Hello, I am Bauer, I come from China, My ID is c0c02dcc-94aa-11e9-972c-5ce0c5e8bcf0
"""
私有属性不能通过类直接访问,也不能通过实例对象直接访问,但私有属性可以通过成员方法进行访问。
私有属性可以通过成员方法或是实例对象._类名__私有变量名
的方式来访问。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
def get_id(self):
return self.__id
if __name__ == "__main__":
bauer = Person("Bauer")
bauer.hello()
print(bauer._Person__id)
print(bauer.get_id())
"""
output:
Hello, I am Bauer, I come from China, My ID is c0c02dcc-94aa-11e9-972c-5ce0c5e8bcf0
354547ae-94ab-11e9-a52c-5ce0c5e8bcf0
354547ae-94ab-11e9-a52c-5ce0c5e8bcf0
"""
四、类的特殊属性
Python的类中有一些内置的、特殊的属性,其名称以双下划线__
开头且以双下划线__
结尾。特殊属性不是私有属性,可以在类的外部通过实例对象去直接访问,且都有着各自特殊的意义。
__doc__
表示类的描述信息。
__module__
表示当前操作的对象对应的类的定义所在的模块名。
__class__
表示当前操作的对象对应的类名。
__dict__
是一个字典,保存类的所有的成员(包括属性和方法)或实例对象中的所有成员属性。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def __hello(self): # 私有方法
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
def say_hello(self): # 成员方法/实例方法
self.__hello()
@classmethod
def get_nationality(cls): # 类方法
return cls.nationality
@staticmethod
def add(a, b): # 静态方法
return a + b
@property
def id(self):
return self.__id
if __name__ == "__main__":
bauer = Person("Bauer")
print("Person Object")
print(bauer.__class__)
print(bauer.__doc__)
print(bauer.__dict__)
print(bauer.__module__)
print("Person Class")
print(Person.__class__)
print(Person.__doc__)
print(Person.__dict__)
print(Person.__module__)
"""
output:
Person Object
<class '__main__.Person'>
None
{'name': 'Bauer', '_Person__id': 'f545f99a-94b5-11e9-aa3f-5ce0c5e8bcf0'}
__main__
Person Class
<class 'type'>
None
{'__module__': '__main__', 'nationality': 'China', '__init__': <function Person.__init__ at 0x7f49941d5d90>, '_Person__hello': <function Person.__hello at 0x7f49941d5e18>, 'say_hello': <function Person.say_hello at 0x7f49941d5ea0>, 'get_nationality': <classmethod object at 0x7f499b470be0>, 'add': <staticmethod object at 0x7f499b470c18>, 'id': <property object at 0x7f499b464908>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
__main__
"""
实例对象.__dict__
和 类.__dict__
的值是不同的,实例对象.__dict__
的值中只包含成员属性和私有属性,类.__dict__
的值中包含类的类属性和所有方法;
__module__
和__class__
的值可用于反射来实例化一个类的对象。
五、类的方法
1、成员方法
成员方法通过类的实例对象去访问,第一个参数必须是当前实例对象,通常写为self;但也可以通过类名来调用成员方法,此时需要手动的传递一个类的实例对象给成员方法的self参数。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
if __name__ == "__main__":
bauer = Person("Bauer")
bauer.hello()
Person.hello(bauer)
"""
output:
Hello, I am Bauer, I come from China, My ID is 0e4d0606-94af-11e9-a958-5ce0c5e8bcf0
Hello, I am Bauer, I come from China, My ID is 0e4d0606-94af-11e9-a958-5ce0c5e8bcf0
"""
2、私有方法
私有方法是以双下划线开头的成员方法。私有方法只能在实例方法内部访问,且不能被子类继承;私有方法的第一个参数也必须是当前实例对象本身,通常写为self。通常,前后加双下划线的命名方式用于Python内置的方法,不推荐自定义方法使用。如果开发者以前后加双下划线的方式命名成员方法,则相应成员方法是公有的。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def __hello(self): # 私有方法
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
def say_hello(self): # 成员方法/实例方法
self.__hello()
if __name__ == "__main__":
bauer = Person("Bauer")
bauer.say_hello()
"""
output:
Hello, I am Bauer, I come from China, My ID is 0e4d0606-94af-11e9-a958-5ce0c5e8bcf0
"""
3、类方法
类方法是以@classmethod
来装饰的成员方法,类方法要求第一个参数必须是当前类。类方法可通过实例对象进行访问,还可以直接通过类名去访问,且第一个参数表示的是当前类,通常写为cls。类方法只能访问类属性,不能访问实例属性,因此第一个参数传递的是代表当前类的cls,而不是表示实例对象的self。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def __hello(self): # 私有方法
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
def say_hello(self): # 成员方法/实例方法
self.__hello()
@classmethod
def get_nationality(cls): # 类方法
return cls.nationality
if __name__ == "__main__":
bauer = Person("Bauer")
print(bauer.get_nationality())
print(Person.get_nationality())
"""
output:
China
China
"""
4、静态方法
静态方法是以@staticmethod
来装饰的成员方法,静态方法通常通过类名进行访问,也可以通过类的实例对象进行访问。本质上,静态方法已经与类没有任何关联,因为静态方法不要求必须传递实例对象或类参数。
静态方法内部可以访问类变量,可以直接使用ClassName.Varaible_Name
方式对类变量进行访问。
静态方法对参数没有要求,因此可以任意给静态方法定义参数,如果给静态方法定义表示当前类的参数,那么就可以访问类属性;如果给静态方法定义了表示当前类的实例对象的参数,那么就可以访问实例属性;如果没有给静态方法定义当前类参数或当前实例参数,那么就不能访问类或实例对象的任何属性。
import uuid
class Person(object):
sum = 0
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
Person.sum += 1
def __hello(self): # 私有方法
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
def say_hello(self): # 成员方法/实例方法
self.__hello()
@classmethod
def get_nationality(cls): # 类方法
return cls.nationality
@staticmethod
def add(a, b): # 静态方法
return a + b
@staticmethod #静态方法,内部使用类变量
def counter():
return Person.sum
@staticmethod
def get_counter(cls): #静态方法,传递当前类
return cls.sum
if __name__ == "__main__":
bauer = Person("Bauer")
print(bauer.add(1, 2))
print(Person.add(1, 2))
print(Person.counter())
print(Person.get_counter(Person))
"""
output:
3
3
1
1
"""
5、属性方法
属性方法是以@property
来装饰的成员方法,是以访问实例属性的方式对实例属性进行访问的成员方法;属性方法第一个参数必须是当前实例对象,且属性方法必须要有返回值。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def __hello(self): # 私有方法
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
def say_hello(self): # 成员方法/实例方法
self.__hello()
@classmethod
def get_nationality(cls): # 类方法
return cls.nationality
@staticmethod
def add(a, b): # 静态方法
return a + b
@property
def id(self):
return self.__id
if __name__ == "__main__":
bauer = Person("Bauer")
print(bauer.id)
# print(bauer.id()) # TypeError: 'str' object is not callable
"""
output:
631baef4-94b3-11e9-babe-5ce0c5e8bcf0
"""
Python中属性方法通常用于在属性方法内部进行一系列的逻辑计算,最终将计算结果返回。
6、方法的绑定
类内部定义的方法,在没有被任何装饰器修饰的情况下,是为了绑定到对象的,self关键字含有自动传值的过程,不管写不写self。 默认情况下,在类内部定义的方法都是绑定到对象的方法。 绑定方法绑定到谁的身上,谁就作为第一个参数进行传入,绑定到类的方法给对象使用是没有任何意义的。
绑定到对象的方法,调用的时候会将对象参数自动传入;绑定到类的方法,调用的时候会将类作为参数自动传入。
静态方法是非绑定方法,不与类或对象绑定,谁都可以调用,没有自动传值效果。非绑定方法不与类或对象绑定,类和对象都可以调用,但没有自动传值。
# -*- coding:utf-8 -*-
import time
import hashlib
import pickle
import os
"""
HOST = "127.1.1.1"
PORT = 3306
DB_PATH = r"/usr/lib/mysql/db"
"""
class MySQL:
HOST = "127.1.1.1"
PORT = 3306
DB_PATH = r"/var/lib/mysql"
@staticmethod
def create_id():
m = hashlib.md5(str(time.perf_counter()).encode("utf-8"))
return m.hexdigest()
def __init__(self,host,port):
self.id = self.create_id()
self.host = host
self.port = port
@classmethod
def from_conf(cls):
return cls.HOST, cls.PORT
def save(self):
file_path = r"%s%s%s"%(MySQL.DB_PATH,os.sep,self.id)
#将对象以二进制的形式写到磁盘
pickle.dump(self,open(file_path,"wb"))
def get(self):
file_path = r"%s%s%s" % (MySQL.DB_PATH, os.sep, self.id)
return pickle.load(open(file_path,"rb"))
if __name__ == '__main__':
conn1 = MySQL("127.0.0.1","3306")
print(conn1.id)
conn1.save()
result = conn1.get()
print(result.id)
六、类的特殊方法
Python的类中有一些内置的、特殊的方法,其名称是以双下划线__
开头且以双下划线__
结尾。特殊方法不是私有方法,可以在类的外部通过实例对象去直接访问,且都有着各自特殊的意义。
1、init构造方法
__init__
方法是类构造函数,是类的特殊的方法,在创建类对象时自动调用,不能有返回值。定义如下:
def __init__(self):
pass
__init__
方法的第一个参数必须是创建的实例本身,通常推荐使用self。类的实例属性、私有属性必须在__init__
方法进行声明。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
print(self.name, "__init__")
if __name__ == "__main__":
bauer = Person("Bauer")
"""
output:
Bauer __init__
"""
2、del析构方法
__del__
是类的析构方法,当对象在内存中被释放,会自动触发执行__del__
方法,如实例对象的作用域退出时,或者执行 del 实例对象操作时。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
print(self.name, "__init__")
def __del__(self):
print(self.name, "__del__")
if __name__ == "__main__":
bauer = Person("Bauer")
del bauer
"""
output:
Bauer __init__
Bauer __del__
"""
3、str
如果类中定义了__str__
方法,那么在打印对象时默认输出__str__
方法的返回值,否则会打印出实例对象的内存地址。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
print(self.name, "__init__")
def __del__(self):
print(self.name, "__del__")
def __str__(self):
return "name: %s, nationality: %s, id: %s" % (self.name, self.nationality, self.__id)
if __name__ == "__main__":
bauer = Person("Bauer")
print(bauer)
"""
output:
Bauer __init__
name: Bauer, nationality: China, id: 0a9a80c2-94c0-11e9-891d-5ce0c5e8bcf0
Bauer __del__
"""
4、getitem、setitem、delitem
__setitem__
、__getitem__
、__delitem__
用于索引操作,如对字典的操作,分别表示设置、获取、删除某个条目、数据。可以通过__setitem__
,__getitem__
、__delitem__
方法来定义一个类对字典进行封装,从而可以对字典中key的操作进行控制,尤其是删除操作。
如果一个类实现了__setitem__
,__getitem__
、__delitem__
方法,就可以执行一些字典操作。
class ChineseDict(object):
def __init__(self, init=None):
self.__dict = init if init is not None else {}
def __setitem__(self, key, value):
print('__setitem__', key)
self.__dict[key] = value
def __getitem__(self, item):
print('__getitem__', item)
return self.__dict.get(item, None)
def __delitem__(self, key):
print('__delitem__', key)
if key is not None and key.startswith('wh'):
print('You can not delete this item ')
return None
return self.__dict.pop(key, None)
if __name__ == "__main__":
dic = ChineseDict(init={'name': 'Bauer', 'nationality': 'China', "age": 23})
print(dic["name"], dic["nationality"], dic["age"])
del dic["age"]
print(dic["age"])
"""
output:
__getitem__ name
__getitem__ nationality
__getitem__ age
Bauer China 23
__delitem__ age
__getitem__ age
None
"""
5、new
__new__
方法会在__init__
方法前被执行,会创建并返回一个新的实例对象,然后传递给__init__
。__new__
不是一个成员方法,而是一个静态方法。
在Python中,一切皆对象,在新式类中,为了将类型(int,str,float等)和类统一,所有的类都是type类型的对象。在类中有一个属性 __metaclass__
可以指定当前类由哪个类进行实例化。而创建对象过程中,构造函数不是__init__
方法,而是__new__
方法,__new__
方法会返回一个对象,即对象构造函数。
类实例化对象内部实现过程的代码段:
class PersonType(type):
def __init__(cls, what, bases=None, dic=None):
super(PersonType, cls).__init__(what, bases, dic)
def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls)
cls.__init__(obj, *args, **kwargs)
return obj
class Dog:
__metaclass__ = PersonType
def __init__(self,name,age):
self.name=name
self.age=age
def __new__(cls, *args, **kwargs):
return object.__new__(cls)
if __name__ == "__main__":
obj = Dog("Dog", 3)
print(obj.name, obj.age)
"""
output:
Dog 3
"""
6、call
类中定义__call__
方法时,类对象实例可以作为一个函数去调用,而函数的调用方式是函数名()。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def __call__(self, *args, **kwargs):
print("name: ", self.name, "args: ", *args)
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
if __name__ == "__main__":
bauer = Person("Bauer")
bauer("China", 26)
"""
output:
name: Bauer args: China 26
"""
七、类的继承
1、派生类定义
Python中类的继承按照父类中的方法是否已实现可分为两种:
实现继承 :指直接继承父类的属性和已定义并实现的的方法;
接口继承 :仅继承父类类的属性和方法名称,子类必须自行实现方法的具体功能代码。
如果是根据要继承的父类的个数来分,有可以分为:
单继承: 只继承1个父类。
多继承: 继承多个父类。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def walk(self):
print('%s is walking...' % self.name)
def talk(self):
print('%s is talking...' % self.name)
class Teacher(Person):
def __init__(self, name, age, level, salary):
super(Teacher, self).__init__(name, age)
self.level = level
self.salary = salary
def teach(self):
print('%s is teaching...' % self.name)
class Student(Person):
def __init__(self, name, age, class_):
Person.__init__(self, name, age)
self.class_ = class_
def study(self):
print('%s is studying...' % self.name)
if __name__ == "__main__":
t1 = Teacher('Bauer', 33, 'Senior', 20000)
s1 = Student('Jack', 13, 'A class')
t1.talk()
t1.walk()
t1.teach()
s1.talk()
s1.walk()
s1.study()
"""
output:
Bauer is talking...
Bauer is walking...
Bauer is teaching...
Jack is talking...
Jack is walking...
Jack is studying...
"""
Teacher类 和Student类都继承 Person类,因此Teacher和Student是Person的子类/派生类,而Person是Teacher和Student的父类/基类/超类;
Teacher和Student对Person的继承属于实现继承,且是单继承;
Teacher类继承了Person的name和age属性,及talk()和walk()方法,并扩展了自己的level和salary属性,及teach()方法;
Student类继承了Person的name和age属性,及talk()和walk()方法,并扩展了自己的class属性,及study()方法;
Teacher和Student对Person类属性和方法继承体现了 代码的重用性, 而Teacher和Student扩展的属性和方法体现了 灵活的扩展性;
子类 Teacher 和 Student 也可以在自己的类定义中重新定义父类中的talk()和walk()方法,改变其实现代码,即方法重写override。
2、派生类构造函数
派生类的构造函数需要显式调用父类的构造函数,对父类的属性成员进行初始化,调用父类的构造函数时需要显式传递实例对象self。
子类需要在自己的__init__
方法中的第一行位置调用父类的构造方法,上述代码给出了两种方法:
super(子类名, self).__init__(父类构造参数)
,如super.(Teacher, self).__init__(name, age)
,推荐方式。
父类名.__init__(self, 父类构造参数)
,如Person.__init__(self, name, age)
。
3、isinstance
isinstance可以判断一个变量是否是某一种数据类型,也可以判断对象是否是类的对象或者是类的子类对象。
issubclass用来判断一个类是否是某个类的子类,返回的是一个bool类型数据。
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
class Teacher(Person):
pass
if __name__ == "__main__":
bauer = Teacher("Bauer")
print(isinstance(bauer, Person))
print(issubclass(Teacher, Person))
"""
output:
True
True
"""
八、类的多继承
1、多继承简介
Python支持多层父类继承,子类会继承父类所有的属性和方法,包括父类的父类的所有属性和方法。Python虽然支持多继承,但Python对多继承的支持的也是有限的。
多继承时,使用super只会调用第一个父类的属性方法,因此,要想调用特定父类的构造器只能显式调用父类名.__init__
。
如果父类中有相同的方法名,而在子类使用时未显式指定调用的具体赋类的方法,Python会根据继承顺序从左至右搜索查找父类中是否包含方法。
class A(object):
def __init__(self):
print("class A")
def hello(self):
print('hello, class A')
def func2(self):
print('class A: func2')
class B(A):
def __init__(self):
A.__init__(self)
print("class B")
def hello(self):
print('hello, class B')
class C(A):
def __init__(self):
A.__init__(self)
print("class C")
def hello(self):
print('hello, class C')
class D(B, C):
def __init__(self):
B.__init__(self)
C.__init__(self)
print("class D")
if __name__ == "__main__":
d = D()
d.hello()
print(D.mro())
"""
output:
class A
class B
class A
class C
class D
hello, class B
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
"""
如果子类从多个父类派生,而子类没有自己的构造函数时,按顺序继承,哪个父类在最前面且有自己的构造函数,就继承其构造函数。
class A(object):
def __init__(self):
print("class A")
def hello(self):
print('hello, class A')
def func2(self):
print('class A: func2')
class B(A):
def __init__(self):
A.__init__(self)
print("class B")
def hello(self):
print('hello, class B')
class C(A):
def __init__(self):
A.__init__(self)
print("class C")
def hello(self):
print('hello, class C')
class D(B, C):
pass
if __name__ == "__main__":
d = D()
d.hello()
print(D.mro())
"""
output:
class A
class B
hello, class B
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
"""
如果子类从多个父类派生,而子类没有自己的构造函数时,如果最前面第一个父类没有构造函数,则依次查找后序继承父类的构造函数。
class A(object):
def __init__(self):
print("class A")
def hello(self):
print('hello, class A')
def func2(self):
print('class A: func2')
class B(A):
def hello(self):
print('hello, class B')
class C(A):
def __init__(self):
A.__init__(self)
print("class C")
def hello(self):
print('hello, class C')
class D(B, C):
pass
if __name__ == "__main__":
d = D()
d.hello()
print(D.mro())
"""
output:
class A
class C
hello, class B
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
"""
2、多继承查找顺序
类的属性__mro__
或者方法mro()都能打印出类的继承顺序,super()在执行时查找MRO列表,到列表当前位置的类中去查找其下一个类。
为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配属性的类为止。
在Python 3.x中无论是否显式指定继承object,所有的类都是新式类,在多继承的情况下,经典类查找父类属性或方法的顺序是深度优先,新式类查找父类属性的顺序是广度优先。
super是MRO中的一个类。MRO全称Method Resolution Order,代表类的继承顺序。对于定义的每一个类,Python会计算出一个方法解析顺序(MRO)列表,MRO列表是一个简单的所有基类的线性顺序列表。
MRO列表的构造是通过一个C3线性化算法来实现的,MRO会合并所有父类的MRO列表并遵循如下三条准则:
A、子类会先于父类被检查。
B、多个父类会根据它们在列表中的顺序被检查。
C、如果对下一个类存在两个合法的选择,选择第一个父类。
MRO可以保证多继承情况每个类只出现一次,super().init相对于类名.init,在单继承上用法基本无差,但在多继承上,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次。
单继承时,使用super方法,不能全部传递,只能传父类方法所需的参数,否则会报错;多继承时,使用类名.init方法需要把每个父类全部写一遍,而使用super方法只需一条语句便执行全部父类的方法,因此多继承需要全部传参。
九、类的多态性
多态通常是通过继承接口的方式实现的,Python中没有接口,但Python中可以通过在一个成员方法体中抛出一个NotImplementedError异常来强制继承接口的子类在调用接口方法前必须先实现接口方法。
class Animal(object): # Animal Interface
def __init__(self, name):
self.name = name
def walk(self):
raise NotImplemented('Subclass must implement the abstract method by self')
def talk(self):
raise NotImplemented('Subclass must implement the abstract method by self')
class Dog(Animal):
def talk(self):
print('%s is talking:wang wang...' % self.name)
def walk(self):
print('%s is a Dog,walk by 4 legs' % self.name)
class Duck(Animal):
def talk(self):
print('%s is talking: ga ga...' % self.name)
def walk(self):
print('%s is a Duck,walk by 2 legs' % self.name)
if __name__ == "__main__":
dog = Dog('Trump')
dog.talk()
dog.walk()
duck = Duck('Tang')
duck.talk()
duck.walk()
"""
output:
Trump is talking:wang wang...
Trump is a Dog,walk by 4 legs
Tang is talking: ga ga...
Tang is a Duck,walk by 2 legs
"""
接口的所有子类必须实现接口中定义的所有方法;接口的各个子类在实现接口中同一个方法时,具体的代码实现各不相同,即多态。
十、反射机制
Python中反射机制是通过hasattr、getattr、setattr、delattr四个内置函数实现的,四个内置函数不仅可以用在类和对象中,也可以用在模块等。
hasattr(key)返回的是一个bool值,判断某个成员或者属性在不在类或者对象中。
getattr(key,default=xxx)获取类或者对象的成员或属性,如果不存在,则会抛出AttributeError异常,如果定义了default那么当没有属性的时候会返回默认值。
setattr(key,value)假如有key属性,那么更新key属性,如果没有就添加key属性并赋值value。
delattr(key)删除某个属性。
实例代码如下:
import uuid
class Person(object):
nationality = "China"
def __init__(self, name):
self.name = name
self.__id = str(uuid.uuid1())
def hello(self):
print("Hello, I am %s, I come from %s, My ID is %s" %(self.name, self.nationality, self.__id))
if __name__ == "__main__":
bauer = Person("Bauer")
setattr(bauer, "sex", "Man")
print(getattr(bauer, "name"))
print(getattr(bauer, "nationality"))
print(getattr(bauer, "sex"))
helloFunc = getattr(bauer, "hello")
helloFunc()
if hasattr(bauer, "job"):
print(getattr(bauer, "job"))
delattr(bauer, "sex")
print(getattr(bauer, "name"))
print(getattr(bauer, "nationality"))
print(getattr(bauer, "sex")) # AttributeError: 'Person' object has no attribute 'sex'
十一、单例模式
在面向对象编程中,单例模式是一个类只有一个对象,所有的操作都通过单例对象来完成,实现代码如下:
class Instance:
__instance = None
@classmethod
def get_instance(cls):
if cls.__instance:
return cls.__instance
else:
cls.__instance = Instance
return cls.__instance
obj1 = Instance.get_instance()
print(id(obj1))
obj2 = Instance.get_instance()
print(id(obj2))
# output:
# 35307304
# 35307304
十二、异常处理
1、异常处理简介
Python中使用try except finally组合来实现异常扑捉,except中的Exception是所有异常的父类,异常处理的示例如下:
try:
int("12a") #可能出现异常的代码
except IndexError as e: # 捕捉索引异常的子异常
print("IndexError:",e)
except ValueError as e: # 捕捉value错误的子异常
print("ValueError:",e)
except Exception as e: # 使用Exception捕获,Exception能够捕获所有的异常
print("Exception:",e)
else: # 如果没有异常发生,执行else中的代码块
print("true")
finally: # 不管是否发生异常,在最后都会执行finally中的代码,假如try里面的代码正常执行,先执行else中的代码,再执行finally中的代码
print("finally")
2、自定义异常处理
Exception是所有异常的父类,可以自定义Exception的子类,实现自定义异常处理。
class TypeErrorException(Exception):
def __init__(self, message):
self.message = message
def __str__(self): # 打印异常的时候会调用对象里面的__str__方法返回一个字符串
return self.message
if __name__ == "__main__":
try:
raise TypeErrorException("Type error")
except TypeErrorException as e:
print("TypeErrorException:",e)
except Exception as e:
print("Exception:",e)
else:
print("true")
finally:
print("finally")
3、断言
断言assert一般用在判断执行环境上,只要断言的条件不满足,就抛出异常,后续代码不会被执行。
print("Assert test")
ok = True
result = False
assert ok == result
print("Assert test")
# output:
"""
Assert test
Traceback (most recent call last):
File "test.py", line 6, in <module>
assert ok == result
AssertionError
"""