初学python(对比java语言不同) 第十一篇

版权声明: https://blog.csdn.net/xclltssun/article/details/51177581

Python 类

与其他编程语言相比,Python的类机制用最少的新语法和新语义引入了类。它是C++和Modula-3类机制的混合。
Python的类提供了面向对象编程的所有标准功能: 类继承机制允许有多个基类,继承的类可以覆盖其基类或类的任何方法,方法能够以相同的名称调用基类中的方法。对象可以包含任意数量和种类的数据。和模块一样,类同样具有 Python 的动态性质:它们在运行时创建,并可以在创建之后进一步修改。

用 C++ 术语来讲,通常情况下类成员是公有的,所有的成员函数都是虚拟的。与 Modula-3 一样,在成员方法中没有简便的方式引用对象的成员:方法函数的声明用显式的第一个参数表示对象本身,调用时会隐式地引用该对象。与 Smalltalk 一样,类本身也是对象。这给导入类和重命名类提供了语义上的合理性。与 C++ 和 Modula-3 不同,用户可以用内置类型作为基类进行扩展。此外,像 C++ 一样,类实例可以重定义大多数带有特殊语法的内置操作符(算术运算符、 下标等)。


名称和对象

对象是独立的,多个名字可以绑定到同一个对象。这在其他语言中称为别名。第一次粗略浏览Python时经常不会注意到这个特性,而且处理不可变的基本类型(数字,字符串,元组)时忽略这一点也没什么问题。然而,在Python代码涉及可变对象如列表、 字典和大多数其它类型时,别名可能具有意想不到语义效果。这通常有助于优化程序,因为别名的行为在某些方面类似指针。例如,传递一个对象的开销是很小的,因为在实现上只是传递了一个指针;如果函数修改了参数传递的对象,调用者也将看到变化。


作用域 
作用域是Python程序中可以直接访问一个命名空间的代码区域。这里的“直接访问”的意思是用没有前缀的引用在命名空间中找到的相应的名称。
虽然作用域的确定是静态的,但它们的使用是动态的。程序执行过程中的任何时候,至少有三个嵌套的作用域,它们的命名空间是可以直接访问的:
1首先搜索最里面包含局部命名的作用域
2其次搜索所有调用函数的作用域,从最内层调用函数的作用域开始,它们包含非局部但也非全局的命名
3倒数第二个搜索的作用域是包含当前模块全局命名的作用域
4最后搜索的作用域是最外面包含内置命名的命名空间


类定义语法

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>
类对象
类对象支持两种操作:属性引用和实例化。

class MyClass:
    """A simple example class"""
    i = 12345
    def f(self):
        return 'hello world'
那么MyClass.i和MyClass.f是有效的属性引用,分别返回一个整数和一个函数对象。也可以对类属性赋值,你可以通过给 MyClass.i 赋值来修改它。__doc__ 也是一个有效的属性,返回类的文档字符串: "A simple example class"。


类的实例化 使用函数的符号。可以假设类对象是一个不带参数的函数,该函数返回这个类的一个新的实例。

x = MyClass()


实例化操作(“调用”一个类对象)将创建一个空对象。很多类希望创建的对象可以自定义一个初始状态。因此类可以定义一个名为__init__()的特殊方法,像下面这样:

def __init__(self):
    self.data = []

实例对象
实例对象唯一可用的操作就是属性引用。有两种有效的属性名:数据属性和方法。
调用x.f()完全等同于MyClass.f(x)。

方法的工作原理。引用非数据属性的实例属性时,会搜索它的类。如果这个命名确认为一个有效的函数对象类属性,就会将实例对象和函数对象封装进一个抽象对象:这就是方法对象。以一个参数列表调用方法对象时,它被重新拆封,用实例对象和原始的参数列表构造一个新的参数列表,然后函数对象调用这个新的参数列表。


类和实例变量
一般来说,实例变量用于对每一个实例都是唯一的数据,类变量用于类的所有实例共享的属性和方法。
在名称和对象讨论的,可变对象例如列表和字典,共享数据可能带来意外的效果。


Java中的数据属性是通过[实例化对象.属性]名调用的。为了实现javabean规则,一般设定为私有,添加get/set方法。

调用方法是用过[实例化对象.方法名(参数...)]或者无参数[实例化对象.方法名()]

Python数据属性和方法属性名字可以一样,所以数据属性会覆盖同名的方法属性;为了避免意外的命名冲突,这在大型程序中可能带来极难发现的 bug,使用一些约定来减少冲突的机会是明智的。可能的约定包括大写方法名称的首字母,使用一个小写的独特字符串(也许只是一个下划线)作为数据属性名称的前缀,或者方法使用动词而数据属性使用名词。


数据属性可以被方法引用,也可以由一个对象的普通用户使用。换句话说,类是不能用来实现纯抽象数据类型。事实上Python中不可能强制隐藏数据—那全部基于约定。


通常方法的第一个参数称为self。这仅仅是一个约定:名字self对 Python 而言绝对没有任何特殊含义。但是请注意:如果不遵循这个约定,对其他的 Python 程序员而言你的代码可读性就会变差。


Python继承

class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>
BaseClassName必须与派生类定义在一个作用域内。用其他任意表达式代替基类的名称也是允许的。

class DerivedClassName(modname.BaseClassName):


派生类定义的执行过程和基类是相同的。类对象创建后,基类会被保存。这用于解析属性的引用:如果在类中找不到请求的属性,搜索会在基类中继续。如果基类本身是由别的类派生而来,这个规则会递归应用。

派生类的实例化没有什么特殊之处:DerivedClassName()创建类的一个新的实例。方法的引用按如下规则解析:搜索对应的类的属性,必要时沿基类链逐级搜索,如果找到了函数对象这个方法引用就是合法的。

派生的类能够重写其基类的方法。因为方法调用本对象中的其它方法时没有特权,基类的方法调用本基类的方法时,可能实际上最终调用了派生类中的覆盖方法。


Python有两个与继承相关的内置函数:
使用isinstance()来检查实例类型:isinstance(obj, int)只有obj.__class__是int或者是从int派生的类时才为True。
使用issubclass()来检查类的继承: issubclass(bool, int)是True因为bool是int的子类。然而,issubclass(unicode, str)是False因为unicode不是str的一个子类(它们只是共享一个共同的祖先,basestring)。


Java 不支持多继承

多继承
Python 也支持一定限度的多继承形式。具有多个基类的类定义如下所示:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>
唯一的规则是深度优先,从左到右。因此,如果在DerivedClassName中找不到属性,它搜索Base1,然后(递归)基类中的Base1,只有没有找到,它才会搜索base2,依此类推。

对于新式类,动态调整顺序是必要的,因为所有的多继承都会有一个或多个菱形关系(从最底部的类向上,至少会有一个父类可以通过多条路径访问到)。例如,所有新式类都继承自object,所以任何多继承都会有多条路径到达object。为了防止基类被重复访问,动态算法线性化搜索顺序,每个类都按从左到右的顺序特别指定了顺序,每个父类只调用一次。

在Python中不存在只能从对象内部访问的“私有”实例变量。然而,有一项大多数Python代码都遵循的公约:带有下划线前缀的名称应被视为非公开的 API 的一部分(无论是函数、 方法还是数据成员)。它应该被当做一个实现细节。

猜你喜欢

转载自blog.csdn.net/xclltssun/article/details/51177581