Python - 类
类的三个基本特征:封装、继承、多态。
封装:将客观事物的共性(属性、方法)抽象归类
继承:OOP的主要功能,可以通过“继承”与“组合”的方式实现
多态:相同方法的不同表现,方法的覆盖与重载
魔法方法:__init__(self,[]) 类实例化过程中自动调用的方法,用于初始化类属性或者对象属性。
公有、私有:
类的属性或者方法分为公有与私有,Python中没有用于区分的关键字,通过双下划线定义私有。但是这种定义是一种 name mangling (姓名重组)技术,即只是内部将类的方法或者属性按照特定的规则进行了重命名。新的私有属性或方法的名称为: _classname__attribute。如下代码测试结果显示,当声明为私有时,无法通过该变量或方法的原始名称进行访问,会有 has no attribute的异常。通过内置属性__dict__可对类的变量进行查看,发现私有属性已经被重命名为 _Test__name,而实例对象仍然能够访问该属性。
1 class Test(): 2 __name = '已经崩盘了' 3 def __init__(self): 4 pass 5 6 t = Test() 7 #t.name #'Test' object has no attribute 'name' 8 #t.__name #'Test' object has no attribute '__name' 9 print(Test.__dict__) # 调用 内置属性 查看类的全部变量 10 t._Test__name 11
------------------------------------------------------------------------------------------------------------------------------------------------- 12 {'__module__': '__main__', '_Test__name': '已经崩盘了',
'__init__': <function Test.__init__ at 0x00B057C8>,
'__dict__': <attribute '__dict__' of 'Test' objects>,
'__weakref__': <attribute '__weakref__' of 'Test' objects>,
'__doc__': None} 13 14 '已经崩盘了'
父类 基类 超类:
父类又叫做超类(super)。子类继承父类时需要对父类进行初始化。因此需要主动调用父类的 __init()__方法,调用父类__init__()方法有两种方式:如下代码第 9 行 与第 10 行。
为什么子类继承父类后需要调用父类的__init__()方法,因为子类中也存在一个 __init__()方法,会对父类的方法进行覆盖,从而无法使用父类__init__()中的属性或方法。
1 import random 2 3 class Father(): 4 def __init__(self): 5 self.x = random.randint(1,10) 6 7 class Child(Father): 8 def __init__(self): 9 super().__init__() 10 #Father.__init__(self) 11 pass 12 13 c = Child() 14 c.x
类的组合:
当几个类之间存在关系,但又很难构成继承关系时,可以通过组合的方式让类横向产生关系。如下代码所示,通过在类中实例化其他类实现类的组合。
class A(): def __init__(self): print("Class A") self.x = 10 class B(): def __init__(self): print('Class B') class Combine(): def __init__(self): self.a = A() #在类中对其他类进行实例化 self.b = B() # 用于关联类横向之间的关系 c = Combine() print(c.a.x) ############################## Class A Class B 10
Mix-in
属性名与方法名相同,会发生覆盖:
当定义属性与方法时,需要注意,不能使属性名与方法名相同,否则后先定义的会被覆盖。原因在于,一切皆对象导致无法做出区分。
1 class A(): 2 x = 10 3 def x(self): 4 print('This is a function!') 5 6 a = A() 7 8 a.x() 9 a.x 10 11 ############################### 12 This is a function! 13 14 <bound method A.x of <__main__.A object at 0x003A2B90>>
绑定:严格要求方法需要有实例才能被调用,这种限制就是Python所谓的绑定概念。
类通过参数 self 进行绑定,self表示对象,即当方法中形参包含该参数时,调用时必须为该方法传入一个对象参数,没有则不用(类中不允许这种情况,但是不会报错)。而self做为一个形参可以使用其他名称代替,self并不是一个关键字。
测试代码如下,注意对象于类的变量列表,由于x,y均为 实例变量,即只有实例化属于对象所有,所以 x,y不会出现在类的变量列表中,而对象为调用setXY函数之前,对象也不存在x,y变量,这很好理解,因为代码还未执行,更不会产生赋值操作(Python赋值即定义)。fun()函数不存在形参,因此可以被类调用,相反 setXY不能被类调用。
class A(): def setXY(self,x,y): self.x = x self.y = y def fun(): print('This is s function') a = A() #实例化一个对象 a #分别打印类的变量与对象的变量 print("类的变量列表为:") print(A.__dict__) print('对象的变量列表为:') print(a.__dict__) # 对象调用 函数 a.setXY(10,20) # 查看各自变量 print(A.__dict__) print(a.__dict__) # 类调用函数 A.fun() #A.setXY(1,2) # 抛出异常 缺少参数 y A.setXY(a,1,2) #原因在于缺少了 对象这个参数 print(A.__dict__) print(a.__dict__) print(a.x,a.y) ################################################# 类的变量列表为: {'__module__': '__main__', 'setXY': <function A.setXY at 0x00B05B28>, 'fun': <function A.fun at 0x00B05858>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} 对象的变量列表为: {} {'__module__': '__main__', 'setXY': <function A.setXY at 0x00B05B28>, 'fun': <function A.fun at 0x00B05858>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} {'x': 10, 'y': 20} This is s function {'__module__': '__main__', 'setXY': <function A.setXY at 0x00B05B28>, 'fun': <function A.fun at 0x00B05858>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} {'x': 1, 'y': 2} 1 2
Python中 一切皆对象,即各种数据类型也不例外,当为对象的属性进行赋值时,若对象的属性不存在则为对象添加改属性,否则进行赋值。
AttributeError:可通过加入判断对赋值的属性进行筛选,避免数据的安全性问题。
相关的BIF:
issubclass(class, classinfo): object 是所有类的基类
isinstance(object, classinfo) #可用于判断变量的类型 字符串,列表等
hasattr(object, name)
getattr(object, name, [default])
setattr(object, name, value)
delattr(object, name)
proprity(fget=none, fset=none, fdel = none) 用于提高代码的封装性。
类中变量的生存空间:
类变量:在类的方法中进行更改时需要使用类进行引用
实例变量:可以在类的方法之间调用
函数局部变量:只能在该方法中使用
测试代码如下所示:
1 class A(): 2 z = 10 3 def __init__(self,x,y): 4 #z += 1 #local variable 'z' referenced before assignment 5 A.z +=1 6 self.x = x 7 self.y = y 8 d = 1 9 10 def fun(self): 11 print('This is s function') 12 print(self.x) 13 print(d) 14 15 a = A(10,20) 16 print(a.z) 17 print(A.z) 18 b = A(1,2) 19 print(b.z) 20 print(A.z) 21 a.fun() #name 'd' is not defined