类, 在学习面向对象我们可以把类当成一种规范, 这个思想就我个人的体会, 感觉很重要, 除了封装的功能外, 类作为一种规范, 我们自己可以定制的规范, 从这个角度来看, 在以后我们学习设计模式的时候, 对设计模式的理解会很有帮助. 其次, 语言中类是抽象的模板, 用来描述具有相同属性和方法的对象的集合, 比如 Animal 类. 而实例是根据类创建出来的一个个具体的 "对象", 每个对象都拥有相同的方法, 但各自的数据可能不同.
Python 使用 class 关键字来定义类, 其基本结构如下:
class 类名(父类列表):
pass
类名通常采用驼峰式命名方式, 尽量让字面意思体现出类的作用. Python 采用多继承机制, 一个类可以同时继承多个父类(也叫基类, 超类), 继承的基类有先后顺序, 写在类名后的圆括号里. 继承的父类列表可以为空, 此时的圆括号可以省略. 但在 Python3 中, 即使你采用类似 classStudent:pass 的方法没有显式继承任何父类的定义了一个类, 它也默认继承 object 类. 因为, object 是 Python3 中所有类的基类.
下面是一个学生类:
- class Student:
- classroom = '101'
- address = 'beijing'
- def __init__(self, name, age):
- self.name = name
- self.age = age
- def print_age(self):
- print('%s: %s' % (self.name, self.age))
可以通过调用类的实例化方法 (有的语言中也叫初始化方法或构造函数) 来创建一个类的实例. 默认情况下, 使用类似 obj=Student()的方式就可以生成一个类的实例. 但是, 通常每个类的实例都会有自己的实例变量, 例如这里的 name 和 age, 为了在实例化的时候体现实例的不同, Python 提供了一个 def__init__(self): 的实例化机制. 任何一个类中, 名字为__init__的方法就是类的实例化方法, 具有__init__方法的类在实例化的时候, 会自动调用该方法, 并传递对应的参数. 比如:
- class Student:
- li = Student("李四", 24)
- zhang = Student("张三", 23)
实例变量和类变量
实例变量:
实例变量指的是实例本身拥有的变量. 每个实例的变量在内存中都不一样. Student 类中__init__方法里的 name 和 age 就是两个实例变量. 通过实例名加圆点的方式调用实例变量.
我们打印下面四个变量, 可以看到每个实例的变量名虽然一样, 但他们保存的值却是各自独立的:
- print(li.name)
- print(li.age)
- print(zhang.name)
- print(zhang.age)
- ------------------------
李四
24
张三
23
类变量:
定义在类中, 方法之外的变量, 称作类变量. 类变量是所有实例公有的变量, 每一个实例都可以访问, 修改类变量. 在 Student 类中, classroom 和 address 两个变量就是类变量. 可以通过类名或者实例名加圆点的方式访问类变量, 比如:
- Student.classroom
- Student.address
- li.classroom
- zhang.address
在使用实例变量和类变量的时候一定要注意, 使用类似 zhang.name 访问变量的时候, 实例会先在自己的实例变量列表里查找是否有这个实例变量, 如果没有, 那么它就会去类变量列表里找, 如果还没有, 弹出异常.
Python 动态语言的特点, 让我们可以随时给实例添加新的实例变量, 给类添加新的类变量和方法. 因此, 在使用 li.classroom = '102'的时候, 要么是给已有的实例变量 classroom 重新赋值, 要么就是新建一个 li 专属的实例变量 classroom 并赋值为'102'. 看下面的例子
- >>> class Student: # 类的定义体
- classroom = '101' # 类变量
- address = 'beijing'
- def __init__(self, name, age):
- self.name = name
- self.age = age
- def print_age(self):
- print('%s: %s' % (self.name, self.age))
- >>> li = Student("李四", 24) # 创建一个实例
- >>> zhang = Student("张三", 23) # 创建第二个实例
- >>> li.classroom # li 本身没有 classroom 实例变量, 所以去寻找类变量, 它找到了!
- '101'
- >>> zhang.classroom # 与 li 同理
- '101'
- >>> Student.classroom # 通过类名访问类变量
- '101'
- >>> li.classroom = '102' # 关键的一步! 实际是为 li 创建了独有的实例变量, 只不过名字和类变量一样, 都叫做 classroom.
- >>> li.classroom # 再次访问的时候, 访问到的是 li 自己的实例变量 classroom
- '102'
- >>> zhang.classroom # zhang 没有实例变量 classroom, 依然访问类变量 classroom
- '101'
- >>> Student.classroom # 保持不变
- '101'
- >>> del li.classroom # 删除了 li 的实例变量 classroom
- >>> li.classroom # 一切恢复了原样
- '101'
- >>> zhang.classroom
- '101'
- >>> Student.classroom
- '101'
类的方法:
Python 的类中包含实例方法, 静态方法和类方法三种方法. 这些方法无论是在代码编排中还是内存中都归属于类, 区别在于传入的参数和调用方式不同. 在类的内部, 使用 def 关键字来定义一个方法.
实例方法
类的实例方法由实例调用, 至少包含一个 self 参数, 且为第一个参数. 执行实例方法时, 会自动将调用该方法的实例赋值给 self.self 代表的是类的实例, 而非类本身. self 不是关键字, 而是 Python 约定成俗的命名, 你完全可以取别的名字, 但不建议这么做.
例如, 我们前面 Student 类中的 print_age()就是实例方法:
- def print_age(self):
- print('%s: %s' % (self.name, self.age))
- # --------------------------
- # 调用方法
- li.print_age()
- zhang.print_age()
静态方法
静态方法由类调用, 无默认参数. 将实例方法参数中的 self 去掉, 然后在方法定义上方加上 @staticmethod, 就成为静态方法. 它属于类, 和实例无关. 建议只使用类名. 静态方法的调用方式.(虽然也可以使用实例名. 静态方法的方式调用)
- class Foo:
- @staticmethod
- def static_method():
- pass
- # 调用方法
- Foo.static_method()
类方法
类方法由类调用, 采用 @classmethod 装饰, 至少传入一个 cls(代指类本身, 类似 self)参数. 执行类方法时, 自动将调用该方法的类赋值给 cls. 建议只使用类名. 类方法的调用方式.(虽然也可以使用实例名. 类方法的方式调用)
- class Foo:
- @classmethod
- def class_method(cls):
- pass
- Foo.class_method()
看一个综合例子:
- class Foo:
- def __init__(self, name):
- self.name = name
- def ord_func(self):
- """定义实例方法, 至少有一个 self 参数"""
- print('实例方法')
- @classmethod
- def class_func(cls):
- """定义类方法, 至少有一个 cls 参数"""
- print('类方法')
- @staticmethod
- def static_func():
- """定义静态方法 , 无默认参数"""
- print('静态方法')
- # 调用实例方法
- f = Foo("Jack")
- f.ord_func()
- Foo.ord_func(f) # 请注意这种调用方式, 虽然可行, 但建议不要这么做!
- # 调用类方法
- Foo.class_func()
- f.class_func() # 请注意这种调用方式, 虽然可行, 但建议不要这么做!
- # 调用静态方法
- Foo.static_func()
- f.static_func() # 请注意这种调用方式, 虽然可行, 但建议不要这么做!
类, 类的方法, 类变量, 类的实例和实例变量在内存中是如何保存的?
类, 类的所有方法以及类变量在内存中只有一份, 所有的实例共享它们. 而每一个实例都在内存中独立的保存自己和自己的实例变量.
创建实例时, 实例中除了封装诸如 name 和 age 的实例变量之外, 还会保存一个类对象指针, 该值指向实例所属的类的地址. 因此, 实例可以寻找到自己的类, 并进行相关调用, 而类无法寻找到自己的某个实例.
Python 类的继承
在 ptyhon 中类一个类是可以同时继承多个类, 语法:
class 类名(父类 1, 父类 2,...)
类体
Python 类继承之深度优先
python 支持多继承, 但对与经典类和新式类来说, 多继承查找的顺序是不一样的.
经典类:
- class P1:
- def foo(self):
- print 'p1-foo'
- class P2 :
- def foo(self):
- print 'p2-foo'
- def bar(self):
- print 'p2-bar'
- class C1 (P1,P2):
- pass
- class C2 (P1,P2):
- def bar(self):
- print 'C2-bar'
- class D(C1,C2):
- pass
实例 d 调用 foo()时, 搜索顺序是 D => C1 => P1
实例 d 调用 bar()时, 搜索顺序是 D => C1 => P1 => P2
换句话说, 经典类的搜索方式是按照 "从左至右, 深度优先" 的方式去查找属性. d 先查找自身是否有 foo 方法, 没有则查找最近的父类 C1 里是否有该方法, 如果没有则继续向上查找, 直到在 P1 中找到该方法, 查找结束.
Python 类继承之广度优先
新式类:
- class P1(object):
- def foo(self):
- print 'p1-foo'
- class P2(object):
- def foo(self):
- print 'p2-foo'
- def bar(self):
- print 'p2-bar'
- class C1 (P1,P2):
- pass
- class C2 (P1,P2):
- def bar(self):
- print 'C2-bar'
- class D(C1,C2):
- pass
实例 d 调用 foo()时, 搜索顺序是 D => C1 => C2 => P1
实例 d 调用 bar()时, 搜索顺序是 D => C1 => C2
来源: https://www.cnblogs.com/xuanhun/p/9444497.html