python面向对象编程实例讲解
python面向对象编程全解。
面向对象技术简介一个类占有一个独立的空间,类中的属性(函数外)叫做类变量,类中的函数,叫做类的方法。类中的方法第一个参数self,表示的是实例对象,不是类。
类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个实例对象所共有的属性和方法。对象是类的实例(对象是类实例化之后的结果)。
类变量:定义在类中且在函数体之外。类变量通常不作为实例变量使用。实例对象无法修改类变量,不过可以读取类变量,只能添加同名属性覆盖类变量
数据成员:类变量和实例变量统称为数据成员。用于处理类及其实例对象的相关的数据。
方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
实例变量:定义在方法中的变量,只庸作为当前实例的属性。
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟”是一个(is-a)”关系(例图,Dog是一个Animal)。
实例化:浅复制类的空间到一个独立的空间。创建一个类的实例,类的具体对象。
方法:类中定义的函数。包含实例方法,类方法,静态方法。
对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
类中的类方法中self表示类本身,实例方法无法调用。
实例对象中的类方法中self表示类本身,实例方法中的self表示实例对象本身
实例化,复制类到一个独立的空间(会复制类中的公共类变量和实例方法、类方法、静态方法到实例对象的独立空间)。实例化的对象可以访问这个独立空间的所有属性和方法。在调用实例方法时将自身传递到函数中。函数的第一个参数self表示的是实例对象本身,self.name表示访问实例对象的属性,如果实例对象不存在就会访问实例空间的类变量,如果类变量也不存在或者不能访问(私有),则就会为实例对象创建这个属性。
创建类#!/usr/bin/python # -*- coding: UTF-8 -*- class Employee: '所有员工的基类' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print("TotalEmployee %d" % Employee.empCount) def displayEmployee(self): print("Name: ", self.name, ", Salary: ", self.salary)
empCount 变量是一个类变量,所有类的实例对象都具有这个成员。你可以在内部类或外部类使用 Employee.empCount 访问。
第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。一个类可以有多个构造函数
self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。Self代表了实例对象,而不是类,那就是:类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
创建实例对象实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。
以下使用类的名称Employee 来实例化,并通过 __init__ 方法接受参数。
"创建 Employee 类的第一个对象" emp1 = Employee("Zara", 2000) "创建 Employee 类的第二个对象" emp2 = Employee("Manni", 5000)操作对象属性
下面是读取对象属性的实例:
#!/usr/bin/python # -*- coding: UTF-8 -*- class Employee: '所有员工的基类' empCount = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def displayCount(self): print("Total Employee %d" % Employee.empCount) def displayEmployee(self): print("Name : ", self.name, ", Salary: ", self.salary)
"创建 Employee 类的第一个对象" emp1 =Employee("Zara", 2000) "创建 Employee 类的第二个对象" emp2 =Employee("Manni", 5000) emp1.displayEmployee() emp2.displayEmployee() print "TotalEmployee %d" % Employee.empCount
以下函数还可以对属性进行读取之外的操作:
getattr(obj,name[, default]) : 访问对象的属性。 hasattr(obj,name): 检查是否存在一个属性。 setattr(obj,name,value): 设置一个属性。如果属性不存在,会创建一个新属性。 delattr(obj,name) : 删除属性。Python内置的类属性
__dict__ : 类的属性(包含一个字典,由类的数据属性组成) __doc__ :类的文档字符串 __name__: 类名 __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod) __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)Python内置的类方法
__init__ 构造函数,在生成对象时调用 __del__ 析构函数,释放对象时使用 __repr__ 打印,转换 __setitem__按照索引赋值 __getitem__按照索引获取值 __len__获得长度 __cmp__比较运算 __call__函数调用 __add__加运算 __sub__减运算 __mul__乘运算 __p__除运算 __mod__求余运算 __pow__称方 我们可以为这些函数进行重写,实现我们想要的功能。例如这里对所有的读写操作进行监督屏蔽。 def __getitem__(self,key): #在字典中获取值时会自动调用 __getitem__函数,设置值时会自动调用__setitem__函数 print("-------------派生类字典读取值") def __setitem__(self,key,value): print("-------------派生类字典设置值") def __getattr__(self,name): #读取类属性(包括继承的属性)会自动执行__getattr__函数,设置属性会自动执行__setattr__函数 print("-------------读取派生类属性") def __setattr__(self,name,value): print("-------------设置派生类属性值")类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。
派生类继承的是基类的类变量(不包含实例变量)和方法。在派生类中调用基类的构造函数时,传入的self是派生类的实例对象的引用。所以此时基类的构造函数新增加的实例变量,直接添加到了派生类实例对象上。所以整个过程中,我们把构造函数想想成初始化函数更好。并且在继承过程中,不存在基类的实例对象。
在python中继承中的一些特点:
1:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
2:在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数
3:Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。
class Parent: # 定义父类 parentAttr = 100 def __init__(self): print("调用父类构造函数") def parentMethod(self): print('调用父类方法') def setAttr(self, attr): Parent.parentAttr = attr def getAttr(self): print("父类属性 :", Parent.parentAttr) class Child(Parent): # 定义子类 def __init__(self): print("调用子类构造方法") # 无论子类还是父类,都要单独写一次_init_ def childMethod(self): print('调用子类方法') def getAttr(self): print('重写父类方法,因为父类方法不能满足需求') c = Child() # 实例化子类 c.childMethod() # 调用子类的方法 c.parentMethod() # 调用父类方法 c.setAttr(200) # 再次调用父类的方法 - 设置属性值 c.getAttr() # 再次调用父类的方法 - 获取属性值
你可以使用issubclass()或者isinstance()方法来检测,一个类或对象是否为其他类或对象的子类。
issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
如果在继承元组中列了一个以上的类,那么它就被称作”多重继承” 。
语法:
派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后。
多态
如果父类方法的功能不能满足需求,可以在子类重写父类的方法。实例对象调用方法时会调用其对应子类的重写后的方法
运算符重载Python同样支持运算符重载,实例如下:
class Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector(%d, %d)' % (self.a, self.b) def __add__(self, other): return Vector(self.a + other.a, self.b + other.b) v1 = Vector(2, 10) v2 = Vector(5, -2) print(v1 + v2)
以上代码执行结果如下所示:
Vector(7,8)类的私有属性及方法
1)类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时self.__private_attrs。
2)类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用self.__private_methods
3)实例
class JustCounter: __secretCount = 0 # 私有变量 publicCount = 0 # 公开变量 def count(self): self.__secretCount += 1 self.publicCount += 1 print(self.__secretCount) # 在内部使用私有化属性,不会产生错误 counter = JustCounter() counter.count() counter.count() print(counter.publicCount) print(counter.__secretCount) # 报错,实例不能访问私有变量单下划线、双下划线、头尾双下划线说明
实例对象无法访问私有的类变量,但是可以添加同名的私有实例变量。
__foo__: 定义的是特列方法,类似 __init__() 之类的。
_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
方法实例方法:只能通过实例调用,实例方法第一个定义的参数只能是实例本身引用。也就是说在实例方法中修改实例变量。可以读取实例变量和类变量。
class Myclass: def foo(self): print(id(self),'foo') a=Myclass()#既然是实例对象,那就要创建实例 a.foo()#输出类里的函数地址 print(id(a))#输出类对象的地址 #结果地址一样
类方法:定义类方法,要使用装饰器@classmethod,定义的第一个参数是能是类对象的引用,可以通过类或者实例直用。类方法用来修改类变量。
class Myclass: @classmethod#类装饰器 def foo2(cls): print(id(cls),'foo2') #类对象,直接可以调用,不需要实例化 print(id(Myclass),'yy') Myclass.foo2()#直接可以调用
静态方法:定义静态方法使用装饰器@staticmethod,没有默认的必须参数,通过类和实例直接调用
class Myclass: @staticmethod#静态方法 def foo3(): print('foo3') Myclass.foo3()#没有参数 a.foo3() #结果foo3全解demo
People.py模块
__all__=["default_age","default","Moniter"] # __all__变量,设置导入模块import*时将会自动引入的函数、变量、类 default_age = 12 #定义模块变量 def set_default_age(age=13): #定义模块函数 print("默认年龄为"+str(age)+"岁") class Parent(object): #定义模块类。()内指定基类,当是object可以省略不写 print("定义了Student类") #定义类时就会执行内部的执行语句 default_name='student' #定义一个类变量 __default_age=12 #函数名或属性名前面为双下划线表示成员私有。只能在当前类或对象空间内使用。 def __init__(self, name1='student1',age1=13): #init是构造函数,self代表类的实例对象。参数可以设置默认值。 self.name=name1 #自动新增两个实例变量 self.age=age1 print("基类构造函数设置了"+self.name) def getname(self): #实例方法,函数名为引用变量,可以进行赋值,即变更函数体。函数引用变量调用时使用(),不带括号代表变量。getname代表函数引用变量,getname()代表代表调用函数 print('基类读取名称'+self.name) return self.name def setname(self,name1): self.name=name1 print("基类设置名称"+self.name) # 派生类继承了基类的类变量和类方法和实例方法(没有实例变量,因为实例变量是在实例以后才存在的) class Child(Parent): #生成派生类,可以多重继承,但是应尽量避免。继承后会包含基类的函数和特性。 def __init__(self,name1="child"): #派生类构造函数不会自动调用基类构造函数 self.name = name1 #在当前实例对象中新增name实例变量 Parent.__init__(self) #两种方法,调用超类的构造函数。基类中就修改了name实例变量,新增了age实例变量 # super(Child,self).__init__(name1) print("派生类构造函数"+self.name) #这里读取的就是最后一次对name的修改(基类中对他的修改) def setname(self, name1): #重写基类中的方法。其实是在派生类中添加了一个新方法,因此在查找此函数时就必用向上查找了。 self.name = "新"+name1 print("基类设置名称" + self.name) def getage(self): #派生类添加新的实例方法 print("派生类读取年龄"+str(self.__default_age)) #派生类是无法读取基类的私有类变量的。因此这句话会报错 @staticmethod #@staticmethod声明函数为静态方法,通过类和实例直接调用 def info(): print("派生类的静态函数") @classmethod #@classmethod声明函数为类方法,第一个参数是能是类对象的引用,可以通过类或者实例直用 def setsex(self): #类方法self表示对类的引用 self.sex='男' #添加类变量 print("派生类设置性别"+self.sex) print('Peopeo类开始运行') #导入模块或执行模块都会执行函数 # 当一个module被执行时,moduel.__name__的值将是"__main__",而当一个 module被其它module引用时,module.__name__将是module自己的名字 if __name__=="__main__": #只有在执行当前模块时才会运行此函数 set_default_age()
调用函数
# 只有有__init__.py文件的文件夹才能是package,才支持引入 import People #引用此模块,就相当于将此模块在此处展开。 # #调用模块变量、模块函数 # People.set_default_age(People.default_age) # print('================================') # #调用模块中的类 parent1 = People.Parent('student1') #调用类的构造函数,实例化一个对象,不需要new,会添加两个实例变量name和age print(parent1.default_name) #读取实例的数据成员(类变量和实例变量) parent1.default_name='luanpeng' #修改实例的数据成员 People.Parent.default_name='Student' #修改类变量 parent1.name='luanpeng' #访问修改实例对象中的属性 parent2 = People.Parent('student2') #调用类的构造函数,实例化一个对象,不需要new print(parent2.default_name) #读取实例的数据成员(类变量和实例变量) # # print(parent1.__default_age) #实例变量没有复制类的私有类变量 parent1.__default_age=14 #添加一个私有的实例变量 print(parent1.__default_age) #可以访问自身的私有实例变量 parent1.getname() #调用类方法 print('================================') child1 = People.Child() #调用类的构造函数,实例化一个对象 print('================================') child1.setname('child') #调用派生类重写或继承的方法 # child1.getage() #调用派生类添加的方法,方法类访问了基类的私有变量,会报错 print('================================') child1.info() #调用静态函数,方法1 People.Child.info() #调用静态函数,方法2 print('================================') # print(child1.sex) #类变量中不存在sex,所以无法查找到,访问出错 child1.setsex() #调用类方法,添加类变量 print(child1.sex) #访问类变量 child2 = People.Child() #使用修改后的类重新实例化一个对象 print(child2.sex) #可以查找到类变量sex print('================================') print(issubclass(People.Child,People.Parent)) # 布尔函数(Child,Parent)判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup) print(isinstance(child1, People.Parent)) #布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。 print('================================')
上一篇: 04-CSS float 浮动