python作为一种解释性语言,其主要的编程方式就是面向对象,而且python的框架django也是主要面向对象的编程。

类(class)和对象(object)

类(class)是用来描述具有相同属性(attribute)方法(method)对象的集合。对象(object)是类(class)的实例。比如学生都有名字和分数,他们有着共同的属性。这时我们就可以设计一个学生类,用来记录学生的名字和分数,并自定义打印出来。

  • 属性(attribute):类里面用于描述所有对象共同特征的变量或数据。比如此例的学生的名字和分数。
  • 方法(method):类里面的函数,用来区别类外面的函数,用来实现某些功能。比如打印出学生的名字和分数。
 # -*- coding:utf-8 -*-
__author__ = 'dapeng'
__date__ = '18-10-26 上午12:08' # 创建一个类
class Student: # 定义学生属性
def __init__(self, name, score):
self.name = name
self.score = score # 定义打印学生信息的方法
def show(self):
print("Name: {}. Score: {}".format(self.name, self.score))

这个案例我们只是定义了一个抽象的类,并没有实例化(Instance)。只有实例化时,才会创建一个对象(object),并为之分配一个存储空间。所以说对象(object)是类(class)的一个实例。

要创建一个具体的学生对象,我么还需:

 student1 = Student(‘Jimmy’, 100)
student1 = Student(‘Tom’, 90)

在这个案例中,Student是类,student1和student2是我们创建的具体的学生对象。当我们输入上述代码时,Python会自动调用默认的__init__初始构造函数来生成具体的对象。关键字self是个非常重要的参数,代表创建的对象本身。

当你创建具体的对象后,你可以直接通过student1.name和student1.score来分别获取学生的名字和分数,也可以通过student1.show()来直接打印学生的名字和分数。

类变量(class variables)与实例变量(instance variables)

假设我们需要在Student类里增加一个计数器number,每当一个新的学生对象(Object)被创建时,这个计数器就自动加1。由于这个计数器不属于某个具体学生,而属于Student类的,所以被称为类变量(class variables)。而姓名和分数属于每个学生对象的,所以属于实例变量(instance variables),也被称为对象变量(object variables)。

这个新student类是这样的:

 # -*- coding:utf-8 -*-
__author__ = 'dapeng'
__date__ = '18-10-26 上午12:08' # 创建一个类
class Student: # number属于类变量,不属于具体实例,定义在方法外
number = 0 # 定义学生属性,初始化方法
def __init__(self, name, score):
self.name = name
self.score = score # 此处演示为错误写法,请忽略
number = number + 1 # 定义打印学生信息的方法
def show(self):
print("Name: {}. Score: {}".format(self.name, self.score))

类变量和实例变量的区别很大,访问方式也不一样。

  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中,但在函数体之外。访问或调用类变量的方法正确做法是类名.变量名或者self.__class__.变量名。self.__class__自动返回每个对象的类名。
  • 实例变量:定义在方法中的变量。,属于某个具体的对象。访问这个变量的正确方式是self.变量名或者对象名.变量名。

纠正后的正确写法为Student.number或者self.__class__.number:

 # -*- coding:utf-8 -*-
__author__ = 'dapeng'
__date__ = '18-10-26 上午12:08' # 创建一个类
class Student: # number属于类变量,不属于具体实例,定义在方法外
number = 0 # 定义学生属性,初始化方法
def __init__(self, name, score):
self.name = name
self.score = score Student.number = Student.number + 1 # 或者self.__class__.number = self.__class__.number + 1 # 定义打印学生信息的方法
def show(self):
print("Name: {}. Score: {}".format(self.name, self.score)) """
实例化:
创建对象
"""
student1 = Student('Jimmy', 100)
student2 = Student('Tom', 90) print(Student.number) # 打印结果:2
print(student1.__class__.number) # 打印结果:2

类方法(classmethod)

正如同有些变量只属于类,有些方法也只属于类,不属于具体的对象。你有没有注意到属于对象的方法里面都有一个self参数, 比如__init__(self), show(self)? self是指对象本身。属于类的方法不使用self参数, 而使用参数cls,代表类本身。另外习惯上对类方法我们会加上@classmethod的修饰符做说明。

下面仍旧以Student为例,不过这次不用print来打印学生数量,而定义一个类方法来打印,看是不是简洁很多。

 # -*- coding:utf-8 -*-
__author__ = 'dapeng'
__date__ = '18-10-26 上午12:08' # 创建一个类
class Student: # number属于类变量,不属于具体实例,定义在方法外
number = 0 # 定义学生属性,初始化方法
def __init__(self, name, score):
self.name = name
self.score = score Student.number = Student.number + 1 # 或者self.__class__.number = self.__class__.number + 1 # 定义打印学生信息的方法
def show(self):
print("Name: {}. Score: {}".format(self.name, self.score)) # 定义类方法,打印学生的数量
@classmethod
def total(cls):
print("Total: {0}".format(cls.number)) """
实例化:
创建对象
"""
student1 = Student('Jimmy', 100)
student2 = Student('Tom', 90) # 打印学生数量,Total:2
Student.total()

类的私有属性(private attribute)和私有方法(private method)

类里面的私有属性私有方法以双下划线__开头。私有属性或方法不能在类的外部被使用或直接访问。我们同样看看学生类这个例子,把分数score变为私有属性,看看会发生什么。

 # -*- coding:utf-8 -*-
__author__ = 'dapeng'
__date__ = '18-10-26 上午12:08' # 创建一个类
class Student: # 定义学生属性,初始化方法
def __init__(self, name, score):
self.name = name
self.__score = score # 定义打印学生信息的方法
def show(self):
print("Name: {}. Score: {}".format(self.name, self.__score)) """
实例化:
创建对象
"""
student1 = Student('Jimmy', 100)
student2 = Student('Tom', 90) student1.show()
student1.__score # 打印出错,该属性不能从外部访问

如果你将score变成__score, 你将不能直接通过student1.__score获取该学生的分数。show()可以正常显示分数,是因为它是类里面的函数,可以访问私有变量。

私有方法是同样的道理。当我们把show()变成,__show()你将不能再通过student1.__show()打印出学生的名字和分数。值得注意的是私有方法必需含有self这个参数,且把它作为第一个参数。

在面向对象的编程中,通常情况下很少让外部类直接访问类内部的属性和方法,而是向外部类提供一些按钮,对其内部的成员进行访问,以保证程序的安全性,这就是封装。

@property的用法与神奇之处

在上述案例中用户不能用student1.__score方式访问学生分数,然而用户也就知道了__score是个私有变量。我们有没有一种方法让用户通过student1.score来访问学生分数而继续保持__score私有变量的属性呢?这时我们就可以借助python的@property装饰器了。我们可以先定义一个方法score(), 然后利用@property把这个函数伪装成属性。见下面例子:

 # -*- coding:utf-8 -*-
__author__ = 'dapeng'
__date__ = '18-10-26 上午12:08' # 创建一个类
class Student: # 定义学生属性,初始化方法
def __init__(self, name, score):
self.name = name
self.__score = score # 定义打印学生信息的方法
@property
def score(self):
print("Name: {}. Score: {}".format(self.name, self.__score)) """
实例化:
创建对象
"""
student1 = Student('Jimmy', 100)
student2 = Student('Tom', 90) student1.score # Name: Jimmy. Score: 100

注意: 一旦给函数加上一个装饰器@property,调用函数的时候不用加括号就可以直接调用函数了 

类的继承(Inheritance)

面向对象的编程带来的最大好处之一就是代码的重用,实现这种重用的方法之一是通过继承(Inheritance)。你可以先定义一个基类(Base class)或父类(Parent class),再按通过class 子类名(父类名)来创建子类(Child class)。这样子类就可以从父类那里获得其已有的属性与方法,这种现象叫做类的继承。

我们再看另一个例子,老师和学生同属学校成员,都有姓名和年龄的属性,然而老师有工资这个专有属性,学生有分数这个专有属性。这时我们就可以定义1一个学校成员父类,2个子类。

 # -*- coding:utf-8 -*-
__author__ = 'dapeng'
__date__ = '18-10-26 上午12:08' class SchoolMember: def __init__(self, name, age):
self.name = name
self.age = age def tell(self):
# 打印个人信息
print('Name: "{}" Age: "{}"'.format(self.name, self.age), end="") class Teacher(SchoolMember): def __init__(self, name, age, salary):
SchoolMember.__init__(self, name, age) # 利用父类进行初始化
self.salary = salary # 方法重写
def tell(self):
SchoolMember.tell(self)
print('Salary: {}'.format(self.salary)) # 创建子类Student
class Student(SchoolMember): def __init__(self, name, age, score):
SchoolMember.__init__(self, name, age)
self.score = score def tell(self):
SchoolMember.tell(self)
print('score: {}'.format(self.score)) teacher1 = Teacher("Jimmy", 40, "$60000")
student1 = Student("Tom", 18, 90) teacher1.tell() # 打印 Name: "Jimmy" Age: "40"Salary: $60000
student1.tell() # 打印 Name: "Tom" Age: "18"score: 90

上述代码中:

  • 在创建子类的过程中,你需要手动调用父类的构造函数__init__来完成子类的构造。

  • 在子类中调用父类的方法时,需要加上父类的类名前缀,且需要带上self参数变量。比如SchoolMember.tell(self), 这个可以通过使用super关键词简化代码。

  • 如果子类调用了某个方法(如tell())或属性,Python会先在子类中找,如果找到了会直接调用。如果找不到才会去父类找。这为方法重写带来了便利。

实际Python编程过程中,一个子类可以继承多个父类,原理是一样的。第一步总是要手动调用__init__构造函数。

super()关键字调用父类方法

在子类当中可以通过使用super关键字来直接调用父类的中相应的方法,简化代码。在下面例子中,学生子类调用了父类的tell()方法。super().tell()等同于SchoolMember.tell(self)。当你使用Python super()关键字调用父类方法时时,注意去掉括号里self这个参数。

 # 创建子类学生Student
class Student(SchoolMember): def __init__(self, name, age, score):
SchoolMember.__init__(self, name, age)
self.score = score def tell(self):
super().tell() # 等同于 SchoolMember.tell(self)
print('score: {}'.format(self.score))

最新文章

  1. IPC-->PIPO
  2. JS-for中的i
  3. BW标准数据源初始化设置
  4. 2. VS使用---HelloWorld
  5. 第二百五十一天 how can I 坚持
  6. CMakeLists实战解读--YouCompleteMe
  7. 25.allegro中模块复用[原创]
  8. [转]fatal error: iostream.h: No such file or directory
  9. PHP数组与对象之间用递归转换
  10. 洛谷3月月赛 R1 Step! ZERO to ONE
  11. python脚本,计算起点终点高程
  12. k8s集群监控(十一)--技术流ken
  13. MySQL :: Fatal error: Can't change to run as user 'mysql'. Please check that the user exists!
  14. ROS中.launch文件的remap标签详解
  15. LeetCode 682 Baseball Game 解题报告
  16. win32多线程程序设计
  17. docker部署PiggyMetrics分布式微服务
  18. 【Python】新建自定义个数的自定义长度名字
  19. MySQL中使用Like模糊查询太慢
  20. IDEA注册码和license服务器附使用方法

热门文章

  1. 基础篇-psql帮助命令
  2. sql server随机排序和随机取出n条数据
  3. (十二)SpringBoot 设置定时任务
  4. Centos 7.5源码编译安装zabbix4.0报fatal error: mysql.h: No such file or directory
  5. thinkPHP--模块分组
  6. matlab各向异性扩散滤波
  7. 前端之CSS布局模型
  8. 装饰者模式--Java篇
  9. jquery select取option的value值发生变化事件
  10. PaaS优点与限制(3)