171112 Learning Python Chapter 26 Class Coding Basics

类产生多个实例对象

  • 类对象提供默认行为,是实例对象的工厂。
  • 类对象来自于语句,实例来自于调用。
  • 模块只有一个副本会导入某一程序中,需要通过reload来更新模块对象。

类对象提供默认行为

  • class 语句创建类对象并将其赋值给变量:class语句一般是在其所在文件导入时才执行
  • class 语句内的赋值语句会创建类的属性:类的属性可由变量名点号运算获取 object.atrribute
  • 类属性提供对象的状态和行为:位于类中的函数def语句会生成方法

实例对象是具体的元素

  • 像函数那样调用类对象会创建新的实例对象:每次调用,内存开辟一个新的空间
  • 每个实例对象继承类的属性并获得自己的命名空间:一开始开辟的内存空间是空的,但是后来会继承并开辟属于自己的类的属性空间
  • 在方法def内对self属性做赋值运算会产生每个实例对象自己的属性:对self属性做赋值运算,会创建或修改实例内的数据,而非类的数据

第一个例子

  • Class开头一行会列出类的名称,后面接一个内嵌并缩进的主体
  • 嵌套的语句是def,定义类要实现导出的行为的函数
  • 在类嵌套的代码块中顶层的复制的任何变量,都会变成类的属性。
    171112 Learning Python Chapter 26 Class Coding Basics_第1张图片
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"

171112 Learning Python Chapter 26 Class Coding Basics_第2张图片

  • 子类继承父类并重新定义父类的方法→重载
  • 子类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]

171112 Learning Python Chapter 26 Class Coding Basics_第3张图片

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__)

(,)
  • 类外定义函数
  • 必须让隐含的实例参数明确化才行,否则,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'))

你可能感兴趣的:(LearningPy)