在面向对象的语言中,类是最重要的一环,python自然拥有类这个机制。python的类机制,与C++,java的区别不是很大,类的大多数的重要特性都被沿用了,一样可以多态,抽象,封装;
在介绍类之前,首先介绍下一下关于python作用域的规则;
1.命名空间:
是从命名到对象的映射。当前命名空间主要是通过 Python 字典实现的,不过通常不关心具体的实现方式(除非出于性能考虑);
以下有一些命名空间的例子:
内置命名(像 abs() 这样的函数,以及内置异常名)集,模块中的全局命名,函数调用中的局部命名。某种意义上讲对象的属性集也是一个命名空间。
关于命名空间需要了解的一件很重要的事就是不同命名空间中的命名没有任何联系,例如两个不同的模块可能都会定义一个名为 maximize 的函数而不会发生混淆--用户必须以模块名为前缀来引用它们。比如:math中有sin函数,可以通过math.sin调用;
2.作用域:
就是一个 Python 程序可以直接访问命名空间的正文区域。 这里的 直接访问 意思是一个对名称的错误引用会尝试在命名空间内查找。
一个例子:
def scope_test(): def do_local(): spam = "local spam"#局部变量 def do_nonlocal(): nonlocal spam spam = "nonlocal spam"#域变量,在整个函数域有效。 def do_global(): global spam spam = "global spam"# spam = "test spam"#全局变量,此处在函数域外有效; do_local() print("After local assignment:", spam) do_nonlocal() print("After nonlocal assignment:", spam) do_global() print("After global assignment:", spam) scope_test() print("In global scope:", spam)#全局变量作用域 ##输出: After local assignment: test spam After nonlocal assignment: nonlocal spam After global assignment: nonlocal spam In global scope: global spam
类定义形式:
class className(superClass): <statement 1> ..... <statement N>
类定义就像函数定义一样,要先执行才能生效。习惯上statement语句是函数定义,不过其他语句也可以。
进入类定义部分后会创建出一个新的命名空间,作为局部作用域,因此所有的赋值成为这个新命名空间的局部变量,特别时函数定义在此处绑定了新的名字
类定义完成时,就创建了一个类对象。也就是类名;
类对象支持两种操作:属性应用和实例化.
属性应用使用:类名.属性;例如类定义如下:
class Myclass: "一个简单的类用于属性介绍" num=123 def fun(self): return 'hello world'
那么我可以Myclass.num和Myclass.fun是有效的属性引用;分别返回一个整数,和一个方法对象;
类定义的实例化:类的实例话使用函数符号 :类名();
例如:x=Myclass()
以上创建了一个新的类的实例,并将该对象赋值给了变量x。此去没有初始化;
很多类都倾向于将对象创建为有初始状态的。因此类可能会定义一个名为 init () 的特殊方法,像下面这样:
def __init__(self): self.data = []
类定义了 init () 方法的话,类的实例化操作会自动为新创建的类实例调用__init__() 方法。所以在下例中,可以这样创建一个新的实例:
x = MyClass()
当 然 , 出 于 弹 性 的 需 要 , init () 方 法 可 以 有 参 数 。 事 实 上 , 参 数 通 过__init__() 传递到类的实例化操作上。
实例对象就是用来操作类的属性引用;有两种引用:数据和方法
数据:和java中的成员类似,其实就是变量;和局部变量一样,数据属性不需要声明,第一次使用他们时就会生成;
比如:
x=Myclass() #Myclass类中没有声明,可以直接使用; x.counter=1
方法:方法是属于一个类的函数,方法引用为:x.fun();
也可以将方法引用赋值给变量这与函数赋值是一样的;
xf=x.fun()#注意self参数,是对象实例化作为第一个参数传给变量的,不需要显示调用;
1实例属性介绍:
给实例绑定属性的方法是通过实例变量,或者通过self变量:
class Student(object): def __init__(self, name): self.name = name s = Student('peace') s.score =40
name和score都是实例属性;
2类属性:
可以直接在class中定义属性,这种属性是类属性,归类所有:
class student: name="peace"
name就是类属性,类的所有实例都可以访问到,并且是指向同一个对象;
没有继承就没有类,而python类的定义如下:
class className(superClass): <statement 1> ..... <statement N> ##其中className就是superclass的派生类。
实例介绍:
class Animal(object): def run(self): print('Animal is running...') class Dog(Animal): pass class Cat(Animal): pass
对于dog与cat来说,Animal就是它的父类。
如果在子类中找不到请求调用的属性,就搜索基类。如果基类是由别的类派生而来,这个规则会递归的应用上去。现在Dog和Cat方法都可以调用run方法了。
也可以增加方法:
class Dog(Animal): def see(self): print('see a dog')
派生类可以覆盖其基类的方法。因为方法调用同一个对象中的其它方法时没有特权,基类的方法调用同一个基类的方法时,可能实际上最终调用了派生类中的覆盖方
法。
class Dog(Animal): def run(self): print('Dog is running...') class Cat(Animal): def run(self): print('Cat is running...')
多态的好处:
def run_twice(animal): animal.run() animal.run() ##测试多态: >>> run_twice(Animal()) Animal is running... Animal is running... >>> run_twice(Dog()) Dog is running... Dog is running...
对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
对扩展开放:允许新增Animal子类;
对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
5.派生类的的实例化与普通类没有什么差别;
Python 有两个用于继承的函数:
• 函数 isinstance() 用于检查实例类型: isinstance(obj, int) 只有在obj.__class__ 是 int 或其它从 int 继承的类型 • 函数 issubclass() 用于检查类继承: issubclass(bool, int) 为 True,因为 bool 是 int 的子类。但是, issubclass(unicode, str) 是 Fals ,因为 unicode 不是 str 的子类(它们只是共享一个通用祖先类 basestring)。
python支持多重继承:
class className(superClass1,superClass1,superClass2.....): <statement 1> ..... <statement N> ##其中className就是superClass1,superClass1,superClass2.....的派生类。
只需要在数据或者方法前面加上_ 两个下划线就行,例如 spam。python独特的命名编码会将 spam替代为 classname__spam
这样在外面就不能正常的 按照:类名.__spam进行调用了。但是写全的话替代为 类名. _classname__spam还是可以的,这时python的缺陷
class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score))
对于外部代码来说,是无法从外部访问实例变量. name和实例变量. score了
可以自己定义异常的派生类,通过rasie进行抛出;
有两种抛出方式:
1.raise Classname()定义的派生类;
2.raise 实例;由异常类实例话而来;
演示如下:
class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B")
要 注 意 的 是 如 果 异 常 子 句 的 顺 序 颠 倒 过 来 ( execpt B 在 最 前 ) , 它 就 会 打 印B,B,B--第一个匹配的异常被触发。