评论

收藏

[python] (3)Py面向对象提高

编程语言 编程语言 发布于:2021-06-24 10:21 | 阅读数:458 | 评论:0

  >几乎任何高级编程语言,均支持面向对象。面向对象属于一种思想,需要程序员为此持续锻炼,巩固。
  >Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的
  这里详细介绍Python的面向对象编程,如果以前没有接触过面向对象的编程语言,
  首先要了解下面向对象的一些基本特征:面向对象技术简介

1、概念定义


  • 类(Class): 用来描述具有相同的属性和方法的对象集合。它定义了该集合中每个对象所共有的属性和方法。


  • 对象(object):通过类定义的数据结构实例。对象包括两个数据成员==属性和方法


  • 实例化:创建一个类的实例,类的具体对象体现,将类通过构造函数转化为实例的过程

2、类的成员


  • 属性:类变量或者实例变量,用于处理类及其实例对象的相关的数据,比如字符串、数组、字典等


  • 类变量:它定义在类中且在方法之外,通常不作为实例变量使用。类变量在整个实例化的对象中是公用的。


  • 实例变量:它定义在方法中指令序列的变量,作用于当前实例。每个实例都有自己的实例变量


  • 方法:类中定义的函数体,即指令序列,往往可以操作实例变量或者完成某个特定的功能


  • 形参:方法名使用的参数,特殊的是self形参,是指类实例指针,构造方法会初始化self,用来指示对象的内存地址


  • 实参:方法体使用的参数,是指令序列可以实际操作和修改的参数,一般用[self.实参]表示
  >理解self:调用类的构造方法,会初始化self为类实例的对象标识,所以一般也称它为 ==> 类实例指针

3、类的创建

  使用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾:
  class ClassName:
  '类的帮助信息'   #类文档字符串
  class_suite      #类体
  例子理解
#!/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
  1)empCount 是一个类变量,它的值将在类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问
  2)第一种方法__init__()方法是类的构造函数或实例化方法,当类被实例化为对象时,默认就会调用该方法
  3)self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
  4)构造函数的变量称为局部变量,self.Name才是实例变量

4、类实例化

  类的实例化方法:init()
  实例化方法:实例化时调用[类()],称为类的构造方法,会初始化[self]为实例标识,然后再调用__init__()初始化方法
  初始化方法:执行初始化方法,它的形参也是构造方法的形参,形参传递给实例变量 ==> self.valume形式
  >核心:实例化方法用来初始化实例变量
  方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖override
  也称为方法的重写。即:需要从子类中重新改写方法。如果要调用父亲同名的方法,使用super()
  比如:
class Father:
     def bor(self):
       pass
class Son(Father):
    def bor(self #重写)
      super(Son, self).bor() #调用父类方法其一
      Father.bor(self, ...)  #调用父类方法其二
      pass
5、类的三大特征

  封装:将事物共有的属性或者共有的方法,集成到类里,可以实例化为不同的对象,就称为封装
  >封装有点类似函数,可以将共同的属性和方法集成一次,以对象标识区分不同的对象,而求同存异
继承:即一个派生类(derived class)继承基类(base class)的字段和方法。可以调用基类的方法  >继承也允许把一个派生类的对象作为一个基类对象对待。
多继承:默认的情况下,所有的面向对象编程中,一个派生类只能继承一个基类,  >但是再C++和Python中,支持一个派生类继承多个基类;
  这个时候基类中相同的方法,派生类会按照继承顺序,依次使用方法;
  但是,如果两个继承基类,又继承同一个根类,那么根类中的方法,最后才执行
多态:Python原生多态,即变量不区分类型  >======================面向对象中高级进阶==============================

6、面向对象的高级知识

a、类的属性字段

  1)类的帮助信息可以通过ClassName.__doc__属性查看
  2)class_suite 类成员是由,类中的方法,数据属性组成
(其一)数据属性:也叫做字段,或者叫变量  实例变量(普通字段)
  >==> 实例化方法中初始化的变量,称为实例变量,作用域是对象中,一般形式时self.valume
  访问方式:必须初始化为对象,通过[对象名.实例变量],定义在self后面
形式变量  >==> 方法名(包括构造方法)的参数列表中的变量,称为形式变量,或者形参,只给编译器使用
  访问方式:无(执行对象方法时,编译器会将传递的参数给实例变量)
类变量(静态字段)  >==> 类方法中初始化的变量,称为类变量,在所有实例化对象之间共享,保存再类中
  访问方式:[类名.类变量名](也可以通过对象访问,[对象名.类变量名])

b、类的实例化和方法

  实例化类其他编程语言中一般用关键字 new,但是在 Python 中类的实例化类似函数调用方式
  >以下使用类的名称 Employee 来实例化,并通过 __init__ 方法接受参数
  >类方法__init__() 是类的构造函数或初始化方法,类被实例化时就会调用这个方法
1)"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
2)"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)
  注意:访问属性
  1)您可以使用点(.)来访问对象的属性和方法。使用如下类的名称访问类变量:
emp1.displayEmployee()
 emp2.displayEmployee()
 print "Total Employee %d" % Employee.empCount。
  2)你也可以使用以下内嵌函数的方式来访问属性:
getattr(obj, name[, default]) : 访问对象的属性。
 hasattr(obj,name) : 检查是否存在一个属性。
 setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
 delattr(obj, name) : 删除属性。
  注意:执行动作
  很清楚:类是由数据成员和方法构成的;数据成员即类属性:方法是类中定义的动作
(其二)方法:也叫做类中的函数  类的实例化方法:init()
  >实例化方法:实例化类用[类()],将初始化[self]为实例标识(指针)和实例变量
  内部先调用__new__()构造方法,然后调用__init__()初始化方法,传递形参
实例方法:  >使用 def 关键字定义在类内部,以self为首形参,并通过实例对象调用的方法。==> 称为实例方法
  实例方法必须包含参数self,调用有通过[对象名.方法名]执行,往往集成类的实现功能
类方法:  >使用 def 关键字定义在类内部,以cls为首形参,并通过类直接调用的方法。 ==> 称为类方法
  类方法前加:装饰器  @classmethod  ,必须包含参数cls,调用通过[类名.类方法]执行
类的静态方法:  >使用 def 关键字定义在类内部,不以self和cls为参数,并通过类直接调用的方法。 ==> 称为静态方法
  静态方法前加:装饰器  @staticmethod ,不使用self、cls作为参数,属于类,调用通过[类名.静态方法]执行
不伦不类的获取属性:  >在类的内部,使用 def 关键字定义一个方法。需要方法前加:装饰器  @property  ==> 用户获取方法值
  特殊属性包含参数self,通过对象名.方法名调用,但不需要括号了
不伦不类的设置属性:  >在类的内部,使用 def 关键字定义一个方法。需要方法前加:装饰器  @获取方法.setter  ==> 用于设置
  方法值, 特殊属性包含参数 self,通过[对象名.方法名]=value调用
不伦不类的删除属性:  >在类的内部,使用 def 关键字定义一个方法。需要方法前加:装饰器  @获取方法.delete  ==> 用于删除
  >方法, 特殊属性包含参数 self,通过[del 对象名.方法名]调用,不是真正的删除,只是一种调用方式
  然后:==================
  很明显:@property 装饰器  ==> 把类中动态方法的访问形式,转化为动态字段的访问方式
比如:调用 emp1.displayEmployee()动态方法调用,可以转化为  emp1.displayEmployee 动态字段调用
还有另一个写法:def bor():  property(fget=bor,fset=xxx, fdel=zzz) ,property方法三个参数,和上面是一样的
c、类的成员修饰符

  公有字段 == 能够在类内定义,类外访问的属性或变量
  >定义:self.value = 'liujian' ;   访问:print(obj.value)
  默认定义的实例变量、类变量等字段,都是公有字段
私有字段 == 在类内定义,类内访问,类外不可以访问的属性或变量  >__private_attrs:两个下划线开头,声明该属性为类的私有字段,不能在类的外部被使用或直接访问。
  在类内部通过定义方法,使用时return  self.__private_attrs。
公有方法  == 能够在类内定义,类外执行的方法或者函数  >定义:def func(): pass  ; 执行: obj.func()
  默认定义的动态方法、静态方法等,都是公有方法
私有方法 ==  在类内定义、类内调用,类外不可以调用执行的方法  >__private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用执行。
  在类的内部调用 self.__private_methods
  注意:私有属性 ==> 是指,仅仅能够在定义的类中,访问或者调用的属性,不能继承
Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName 访问属性,
将如下代码替换以上代码的最后一行代码:
.........................  
print counter._JustCounter__secretCount
  注意:通常你需要在单独的文件中定义一个类
  >类名()==》 调用__init__()方法
  >对象名()==》 调用__call__()方法
  特殊的内部方法:
def __init__(self): 构造方法,当类被初始化为对象时,自动执行
 def __call__(self): 特殊方法,当对象以方法的形式调用时,自动执行,比如obj()
 def __int__(self): 数值转化方法,直接打印,将对象转化为数字
 def __str__(self): 字符串转化方法,直接打印,将对象转化为字符串
 def __del__(self): 析构方法,在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行
 def __add__(self): 对象之间操作,默认会执行第一个对象obj1.__add__()方法,并传递其他对象为参数other
当两个对象相加时,默认执行第一个对象的__add__()方法,并将第二个对象作为参数传递进去
  ==> 在python中,每种类型都是一个类,这种类型的操作支持,都是都是因为类中存在很多特殊方法  对应方法如下:
  >def __getitem__(self, itme) 切片或者索引 ;  def __set__(self, key, value) ; def __delitem__(self, value)
  另外一个重要的特殊修饰符:
  >def __iter__(self) ==> 执行对象的iter方法,获取一个迭代器对象供给for循环迭代(迭代器是另一个类)
  注意:Python内置类属性
__dict__ : 类的自身属性(这是一个字典,由类的数据属性和结构组成)
 __doc__ :类的文档字符串(主要对类进行说明,或者作者简介)
 __name__: 类名,本类的名称
 __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
 __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
单下划线、双下划线、头尾双下划线说明:
_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了
__foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的
  类在实例化时,可以传递变量参数;如果类在实例化后,也可给对象赋值
  1)默认情况里,对象被实例化后,不可以修改,也就是只读,但是有个前提:继承object基类
  2)装饰器 @function_name.setter  ,表示对象没实例化后,可写
  注意:self代表类的实例,而非类
  类的方法与Py函数有一个特别的区别——类方法必须有一个额外的第一个参数名称, 按照惯例它的名称是 self
class Test:
   def prt(self):
     print(self)
     print(self.__class__)
t = Test()
t.prt()
  从执行结果可以很明显的看出:self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
  self 不是 python 关键字,我们把他换成 runoob 也是可以正常执行的:

d、对象的销毁

  又称垃圾回收。Python 使用了引用计数这一简单技术来跟踪和回收垃圾,它内部记录着所有使用中的对象各有多少引用
  原理:一个内部跟踪变量,称为一个引用计数器。
  >1)当对象被创建时, 就创建了一个引用计数;
  2)当这个对象不再需要时, 也就是说, 这个对象的引用计数变为0 时, 它就会被垃圾回收
  但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收
  垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。
  循环引用指的是,两个对象相互引用,但是没有其他变量引用他们。
  这种情况下,Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。
  作为引用计数的补充,垃圾收集器也会留心被分配的总量很大的对象(及未通过引用计数销毁的那些)。
  在这种情况下, 解释器会暂停下来, 试图清理所有未引用的循环
  析构函数 __del__ ,__del__在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行
  所以一个完整的类被定义为:
class Point:
  def __init__( self, x=0, y=0):
   self.x = x
   self.y = y
  def __del__(self):
   class_name = self.__class__.__name__
   print class_name, "销毁"
  
6、类实继承


  面向对象的编程带来的主要好处之一是代码重用,因为类往往能封装一些功能,实现这种重用的方法之一是通过继承机制。
  继承完全可以理解成类之间的类型和子类型关系。需要注意的地方:
  继承语法 :
  >class 派生类名(基类名): //... 基类名写在括号里,基类是在类定义的时候,在元组之中指明的
  在python中继承中的一些特点:
  >1:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用
  >2:调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别在类中调用普通函数时并不需要带上self参数
  >3:Py总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。
  (先在本类中查找调用的方法,找不到才去基类中找)。
  如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。
  语法:派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:
class SubClassName (ParentClass1[, ParentClass2, ...]):
  'Optional class documentation string'
  class_suite
  可以使用issubclass()或者isinstance()方法来检测,类是否继承于某个父类。
  >issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
  >isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
类的继承本质:  面向对象的编程思想,主要体现在类的封装;类 ==> 又称为类型定义,正如str、tune、list、int等
  “类型class”继承为“type”类;换句话说《类的继承,本质上是父类的实例化过程》,推理出类型class,也是实例对象
  那么:
Class A(B) ; 类A,继承 类B ==> 类A,是类B的实例化对象,即A,也是对象;(主要是Type的对象)
   此时,解释器立刻执行 Type.__init__(self, *args, **kwargs),目的是初始化对象A,也就是类A
obj = A() ; 实例化类A,生成真正想要的对象obj  ==> 就是对象A的方法调用,A() ,此刻导致
   类A的父类Type.__call__(self, *args, **kwargs)特殊方法的执行,这里面又会呈现如下调用:
   >a)  self.__new__(self, *args, **kwargs) 方法, ==》这是什么,这是类B的实例化对象A的__new__()函数
   >b)  self.__init__(self, *argv, **kwargs) 方法,==》这是什么,这是类B的实例化对象A的__init__()函数
  由此可见:
  创建对象obj时,通过类A的父类Type.__call__()方法,依次执行了两步:
  >1)使用A.__new__()生成一下新对象
  >2)使用A.__init__()初始化这个新对象
  那么Class A(B)这个步骤,其实也会再次递归到B的继承,B.__new__()方法和B.__init__()方法的执行

7、方法重写

  如果父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Parent:    # 定义父类
  def myMethod(self):
   print '调用父类方法'
class Child(Parent): # 定义子类
  def myMethod(self):
   print '调用子类方法'
c = Child()      # 子类实例
c.myMethod()     # 子类调用重写方法
  列出了一些通用的功能,你可以在自己的类重写:
  >1) __init__ ( self [,args...] )
  构造函数 , 简单的调用方法: obj = className(args)
  >2) __del__( self )
  析构方法, 删除一个对象, 简单的调用方法 : del obj
  >3) __repr__( self )
  转化为供解释器读取的形式,简单的调用方法 : repr(obj)
  >4) __str__( self )
  用于将值转化为适于人阅读的形式,简单的调用方法 : str(obj)
  >5) __cmp__ ( self, x )
  对象比较,简单的调用方法 : cmp(obj, x)
  运算符重载, Python同样支持运算符重载,实例如下:
#!/usr/bin/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
关注下面的标签,发现更多相似文章