类的三个基本特征:封装、继承、多态。
封装:将客观事物的共性(属性、方法)抽象归类
继承: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