类产生多个实例对象
- 类对象提供默认行为,是实例对象的工厂。
- 类对象来自于语句,实例来自于调用。
- 模块只有一个副本会导入某一程序中,需要通过reload来更新模块对象。
类对象提供默认行为
- class 语句创建类对象并将其赋值给变量:class语句一般是在其所在文件导入时才执行
- class 语句内的赋值语句会创建类的属性:类的属性可由变量名点号运算获取 object.atrribute
- 类属性提供对象的状态和行为:位于类中的函数def语句会生成方法
实例对象是具体的元素
- 像函数那样调用类对象会创建新的实例对象:每次调用,内存开辟一个新的空间。
- 每个实例对象继承类的属性并获得自己的命名空间:一开始开辟的内存空间是空的,但是后来会继承并开辟属于自己的类的属性空间。
- 在方法def内对self属性做赋值运算会产生每个实例对象自己的属性:对self属性做赋值运算,会创建或修改实例内的数据,而非类的数据。
第一个例子
- Class开头一行会列出类的名称,后面接一个内嵌并缩进的主体
- 嵌套的语句是def,定义类要实现导出的行为的函数
- 在类嵌套的代码块中顶层的复制的任何变量,都会变成类的属性。
class FirstClass: # define a class object
def setdata(self,value): # define class methods
self.data = value # self is the instance
def display(self):
print(self.data) # self.data:per instance
x = FirstClass()
y = FirstClass()
x.setdata('King Arthur')
y.setdata(3.1415926)
x.display() # make new instances
y.display() # Each is a new namespace
King Arthur
3.1415926
- 程序可以取出、修改或创建其所引用的任何对象的属性。
x.data = "New Value" # Can set/get attributes
x.display( ) # Outside the class too
x.anothername = 'spam' # Can set new attributes here too!
New Value
类通过继承进行定制
- 超类列在类开头的括号中: Class Son(Father)
- 类从其超类中继承属性
- 实例会继承所有可读取类的属性:顺着搜索树向上爬
- 每个object.attribute都会开启新的独立搜索
- 逻辑(类内方法/函数)的修改是通过创建子类,而不是修改超类:在树中较低的子类中重新定义超类的变量名,子类就可取代并定制所继承的行为。
第二个例子
class SecondClass(FirstClass):
def display(self):
print('Current value = "%s"' % self.data )
z = SecondClass()
z.setdata(42)
z.display()
Current value = "42"
- 子类继承父类并重新定义父类的方法→重载
- 子类SecondClass的专有化完全是在FristClass外部完成的,故子类不会影响当前或未来的FirstClass对象
- 类继承可以让我们像这样在外部组件内(也就是在子类内)进行修改,类所支持的扩展和重用通常比函数或模块更好。
x.display() # 子类SecondClass的专有化完全是在FristClass外部完成的,故子类不会影响当前或未来的FirstClass对象
New Value
类是模块内部的属性
- 模板反应了整个文件
- 类知识文件内的语句
- 导入模块,从模块中引用类class
from keras.layers import Dense
class MyDense1(Dense):
def display(self):
print('Learning Python!')
import keras.layers
class MyDense2(Dense):
def display(self):
print('Learning Python!')
- 一个模块可以定义多个类
# food.py
var = 1
def func():
...
class spam:
...
class ham:
...
class eggs:
...
- 模块名和类型相同
class person:
...
import persom # Import module
x = person.person() # class within module
- 避免混淆,类名需要大写
import person
x = person.Person()
类可以截取Python运算符
- 以双下划线命名的方法(_X_)是特殊的例子:Python语言替每种运算和特殊命名的方法之间,定义了固定不变的映射关系。
- 当实例出现在内置运算时,这类方法回自动调用:__add__→+
- 类可覆盖多数内置类型运算
- 运算符覆盖方法没有默认值,而且也不需要
- 运算符可让类与Python的对象模型相集成
- 运算符重载一般给开发人员用,所以一般少用;除非”init方法”,也就是构造函数方法,它是用于初始化对象状态的。
第三个例子
class ThirdClass(SecondClass): # Inherit from SecondClass
def __init__(self,value): # On "ThirdClass(value)"
self.data = value
def __add__(self,other): # On "self+other"
return ThirdClass(self.data+other) # 创建返回新对象,未修改
def __str__(self): # On "print(self),"str()"
return '[ThirdClass:%s]' %self.data
def mul(self,other): # In-place change:named
self.data *= other # 修改了self对象的值
a = ThirdClass('abc') # __init__ called
a.display()
print(a)
Current value = "abc"
[ThirdClass:abc]
b = a+'xyz'
b.display()
print(b)
Current value = "abcxyz"
[ThirdClass:abcxyz]
a.mul(3)
print(a)
[ThirdClass:abcabcabc]
为什么要使用运算符重载(详见29章)
几乎每个实际的类似乎都会出现一个重载方法:__init__构造函数。因为这可以让类立即在其新建的实例内添加属性。
世界上最简单的Python类
class rec:pass
rec.name = 'Bob'
rec.age = 40
help(rec)
Help on class rec in module __main__:
class rec(builtins.object)
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| age = 40
|
| name = 'Bob'
x = rec()
y = rec()
x.name, y.name # name is stored on the class only
('Bob', 'Bob')
赋值操作为对象开辟了新的属性空间,不影响其他对象的继承搜索
x.name = 'Sue' # But assignment changes x only
rec.name, x.name, y.name
('Bob', 'Sue', 'Bob')
命名空间对象的属性通常都是以字典的形式实现的,__dict__属性是针对大多数基于类的对象的命名空间字典
rec.__dict__.keys()
dict_keys(['__module__', '__dict__', '__weakref__', '__doc__', 'name', 'age'])
print(list(x.__dict__.keys())) # x有自己的命名空间name
print(list(y.__dict__.keys())) # y继承搜索rec命名空间中的name,本身不具有name命名空间
['name']
[]
- 每个实例都连接至其类以便于继承,通过__class__查看基于实例的父类
- 类通过__bases__查看其父类
print(x.__class__)
print(rec.__bases__)
<class '__main__.rec'>
(<class 'object'>,)
- 类外定义函数
- 必须让隐含的实例参数明确化才行,否则,Python无法猜测简单函数是否最终会变成类的方法
def upperName(self):
return self.name.upper() ## still needs a self
upperName(x)
'SUE'
rec.method = upperName
print(x.method()) # run method to process x
print(y.method()) # same, but pass y to self
print(rec.method(x)) # Can call through instance or class
SUE
BOB
SUE
Python中的OOP其实就是在已连接命名空间对象内寻找属性而已
类与字典的关系
字典
rec = {}
rec['name']='mel'
rec['age']=45
rec['job']='trainer/writer'
print(rec['name'])
mel
类
class rec:pass
rec.name = 'mel'
rec.age = 45
rec.job = 'trainer/writer'
print(rec.name)
mel
我们产生一个空类的实例来表示每条不同的记录
class rec:pass
pers1 = rec() # an empty class
pers1.name = 'mel'
pers1.job = 'trainer'
pers1.age = '40'
pers2 = rec() # an empty class
pers2.name = 'vls'
pers2.job = 'developer'
pers1.name,pers2.name
('mel', 'vls')
最后,我们可能编写一个更完整的类来实现记录及其处理
class Person:
def __init__(self,name,job):
self.name = name
self.job = job
def info(self):
return (self.name,self.job)
rec1 = Person('mel','trainer')
rec2 = Person('vsl','developer')
rec1.job,rec2.info()
('trainer', ('vsl', 'developer'))