【九】类——1

前面介绍了Python主要的内建对象类型(数字、字符串、列表、元组和字典),以及内建函数和标准库的用法,还有定义函数的方法。现在开始学习如何创建自己的对象。

一.对象

【百度百科】中的定义:

广义:在内存上一段有意义的区域,称作为一个对象。

在C中,具有特定长度的类型,可以称作为对象类型,函数不具有特定长度,所以不是对象类型。

在显式支持面向对象的语言中,“对象”一般是指类在内存中装载的实例,具有相关的成员变量和成员函数(也称为:方法)。

教材上的定义:

对象(object)基本上可以看做数据(特性)以及由一系列可以存取、操作这些数据的方法所组成的集合。

二.对象的特征

1.多态性

“多态性”一词最早用于生物学,指同一种族的生物体具有不同的特性。

在面向对象的程序设计理论中,多态性的定义是:同一操作作用于不同的类的实例,将产生不同的执行结果,即不同的类的对象收到相同的消息时,得到不同的结果。多态是面向对象的程序设计的重要特征之一,是扩展性在“继承”之后的又一重大表现 。对象根据所接受的消息而做出动作,同样的消息被不同的对象接受时可能导致完全不同的行为,这种现象称为多态性。

多态性包含编译时的多态性、运行时的多态性两大类。 即:多态性也分静态多态性和动态多态性两种。

静态多态性:

是指定义在一个类或一个函数中的同名函数,它们根据参数表(类型以及个数)区别语义,并通过静态联编实现,例如,在一个类中定义的不同参数的构造函数。

动态多态性:

是指定义在一个类层次的不同类中的重载函数,它们一般具有相同的函数,因此要根据指针指向的对象所在类来区别语义,它通过动态联编实现。

在用户不作任何干预的环境下,类的成员函数的行为能根据调用它的对象类型自动作出适应性调整,而且调整是发生在程序运行时,这就是程序的动态多态性。即,发出同样的消息被不同类型的对象接收时,有可能导致完全不同的行为。

例:

#加运算符对于整数和字符串都能起作用。
>>> 1+2
3
>>> 'Fish'+'License'
'FishLicense'

 很多函数和运算符都是多态的,写的绝大多数程序可能都是,即便并没有意识到。

2.封装性

封装 (encapsulation):隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别。

封装就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的数据源进行有机的结合,形成“类”,其中数据和函数都是类的成员。

封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。

每个类的实例可以拥有保存自己状态的属性。一个类的实例也可以有改变自己状态的(定义在类中的)方法

每个对象有它自己的状态,对象的状态由它的特性(比如名称)来描述。对象的方法可以改变它的特性。所以就像是一堆的函数(方法)捆在一起,并且给予它们访问变量(特性)的权利,它们可以在函数调用之间保存的值。

3.继承性

“继承”是面型对向软件技术当中的一个概念。如果一个类A继承自另一个类B,就把这个A称为"B的子类",而把B称为"A的父类"。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。

在令子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。另外,为子类追加新的属性和方法也是常见的做法。

有些编程语言支持多重继承,即一个子类可以同时有多个父类,比如C++编程语言;而在有些编程语言中,一个子类只能继承自一个父类,比如Java编程语言,这时可以利用接口来实现与多重继承相似的效果。

在C++语言中,一个派生类可以从一个基类派生,也可以从多个基类派生。从一个基类派生的继承称为单继承;从多个基类派生的继承称为多继承。

比如说有个Shape类,可以用来在屏幕上画出指定的形状。现在需要创建一个叫做Rectangle的类,它不但可以在屏幕上画出指定的形状,而且还能计算该形状的面积。但又不想把Shape里面已经写好的draw方法再写一次。可以让Rectangle从Shape类继承方法。在Rectangle对象上调用draw方法时,程序会自动从Shape类调用该方法。

三.类

1.什么是类

类可以看做是种类或者类型的同义词,所有的对象都属于一个类,称为类的实例。

在面向对象的程序设计中,子类的关系是隐式的,因为一个类的定义,取决于它所支持的方法,类的所有实例都包含这些方法,所以所有子类的实例都有这些方法,定义子类只是定义更多(也有可能是重载已经存在的)的方法的过程。

在软件术语中,被继承的类一般称为“超类”,也有叫做父类。是继承中非常重要的概念,它和子类一起形象地描述了继承的层次关系。

2.创建类

由类体类成员,方法,数据属性组成。

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
  • empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。
  • 第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。
  • self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。

类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。

3.创建实例对象

实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。

以下使用类的名称 Employee 来实例化,并通过 __init__ 方法接收参数。

"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)

4.访问属性

您可以使用点号 . 来访问对象的属性。使用如下类的名称访问类变量:

emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
  • 可以将一个特性绑定到一个普通函数上,这样就不会有特殊的self参数了。
def displayCount(self):
    print "Total Employee %d" % Employee.empCount

def function():
    print "Total Employee is %d" % Employee.empCount

Emplyee.displayCount = function()
    
  •  可以使用其他变量引用同一个方法。
function = Emplyee.displayCount    

四.类的命名空间

所有位于 class 语句中的代码,其实都位于特殊的命名空间中,通常称之为类命名空间,这个命名空间可由类内所有的成员访问。Python 中,编写的整个程序默认处于全局命名空间内,而类体则处于类命名空间内。

  • 1.类名.属性

使用类名.属性 只会寻找类中的静态变量名字
使用对象.属性 会现在对象自己的命名空间中找名字。如果找不到再到类的内存空间中去找

# 只要你使用静态变量,就用类名去调用,命名空间可由类内所有的成员访问。
class Person:
    money = 0

mother = Person()
father = Person()
Person.money += 1000
Person.money += 1000
print(mother.money)    #2000
print(father.money)    #2000
print(Person.money)    #2000
  • 2.写一个类,能统计这个类被多少个对象实例化了,所有的对象共享这个结果,init 静态变量。
class Foo:
    num = 0
    def __init__(self):
        Foo.num += 1

f1 = Foo()
print(Foo.num)   # 1
f2 = Foo()
print(Foo.num)  # 2
print(f1.num)    #  2
  • Python 允许在全局范围内放置可执行代码,当 Python 执行该程序时,这些代码就会获得执行的机会。类似地,Python 同样允许在类范围内放置可执行代码,当 Python 执行该类定义肘,这些代码同样会获得执行的机会。

下面代码示范了在全局空间和类命名空间内分别定义 lambda 表达式:

global_fn = lambda p: print('执行lambda表达式,p参数: ', p)
class Category:
    cate_fn = lambda p: print('执行lambda表达式,p参数: ', p)
# 调用全局范围内的global_fn,为参数p传入参数值
global_fn('fkit')  # ①
c = Category()
# 调用类命名空间内的cate_fn,Python自动绑定第一个参数
c.cate_fn()  # ②

上面程序分别在全局空间、类命名空间内定义了两个 lambda 表达式,在全局空间内定义的 lambda 表达式就相当于一个普通函数,因此程序使用调用函数的方式来调用该 lambda 表达式,并显式地为第一个参数绑定参数值,如上面程序中 ① 号代码所示。

对于在类命名空间内定义的 lambda 表达式,则相当于在该类命名空间中定义了一个函数,这个函数就变成了实例方法,因此程序必须使用调用方法的方式来调用该 lambda 表达式,Python 同样会为该方法的第二个参数(相当于 self 参数)绑定参数值,如上面程序中 ② 号代码所示。

五.python对象销毁(垃圾回收)

Python 使用了引用计数这一简单技术来跟踪和回收垃圾。

在 Python 内部记录着所有使用中的对象各有多少引用。

一个内部跟踪变量,称为一个引用计数器。

当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的引用计数变为0 时, 它被垃圾回收。但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。

a = 40      # 创建对象  <40>
b = a       # 增加引用, <40> 的计数
c = [b]     # 增加引用.  <40> 的计数

del a       # 减少引用 <40> 的计数
b = 100     # 减少引用 <40> 的计数
c[0] = -1   # 减少引用 <40> 的计数

垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。循环引用指的是,两个对象相互引用,但是没有其他变量引用他们。这种情况下,仅使用引用计数是不够的。Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。作为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(及未通过引用计数销毁的那些)的对象。 在这种情况下, 解释器会暂停下来, 试图清理所有未引用的循环。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值