(python)面向对象
一、面向对象概述
要了解面向对象,就需要先了解面向过程的概念,那么什么是面向过程编程呢?最具代表性的就是C语言了,所谓面向过程编程就是在做一件事的时候,需要按步骤进行,第一步干什么,第二步干什么,这种编程方式适合问题规模较小,需要步骤化处理逻辑的业务。
了解了面向过程编程,那么就容易理解面向对象编程了,官方解释是一种认识世界、分析世界的方法论,将万事万物都抽象为类的方法。这种概念较为抽象不方便理解,以生活中的示例为例,所谓面向对象就是你吃鱼,你属于一个对象(实例),鱼也属于一个对象(实例),那么吃就是动作(方法)了。我们可以将具体的事物抽象成对象(实例)(人、鱼是有无数具体的个体抽象出来的),将动作抽象成方法,这种抽象方式就是面向对象编程,而python使用的正是这种编程方式。
之前面向对象有两个概念,类和对象。那么什么是类呢,类是一种抽象的概念,是万事万物的抽象,是一类事物共同特征的集合,也就是属性和方法的集合。现在又出来了属性和方法,所谓属性就是对象状态的抽象,用数据结构来描述,通俗来讲每个人都有名字、身高体重等信息,这些信息是个人的属性,但是,这些信息不属于特定的某一个人,它是抽象的概念,不能保留具体的值;所谓方法就是对象行为的抽象,用操作名和实现该操作的方法来描述,通俗来讲就是类中解决某一实现方法的功能。什么是对象呢?对象就是抽象的类的具体的实例,一个类可以有多个对象(实例),每个对象都有其特有的属性,可以通过具体的实例来实现自己需要实现的方法。
二、面向对象的三要素
1、封装
将数据和操作组装到一起,对外只暴露一些接口,通过接口去访问对象。通俗来讲就是你驾驶一辆汽车,不需要去了解汽车的构造细节,只需要知道使用什么部件怎么驾驶就可以了。
2、继承
可以继承父类的某些方法和属性,也可以重写父类的某些方法和属性实现个性化,多复用性。通俗来讲就是人类继承自动物类,孩子继承父母的特征。有单一继承和多继承。
3、多态
继承自动物类的人类,人类的“吃”操作和猫类的不同。这是面向对象编程最灵活的地方。
三、类的抽象
一、类的定义
1、语法:
class ClassName:
语句块
- 使用class关键字来定义一个类
- 类名必须使用大驼峰的方式来进行命名(每个单词的首字母大写)
- 类在定义完成后,就产生了一个类对象,绑定到了标识符ClassName上
2、举例
class MyClass:
"""A example class"""
x = "abc" # 类属性,类变量 def foo(self): # 类属性,类方法
return "My Class" print(MyClass.x) # abc
print(MyClass.foo) # <function MyClass.foo at 0x000000E545931840>
print(MyClass.__doc__) # A example class
- 类对象:类的定义就会生成一个类对象。
- 类属性:类中的变量和方法都是类的属性。
- 可以通过类名直接调用其中的属性,x、foo都是类的属性,__doc__也是属性。
- foo是方法对象method,不是普通的函数对象function了,它一般要求至少有一个参数,第一个参数就是self(标识符,可以换其他名字,但是不建议换,规范),这个参数位置就留给了self,self指代当前实例本身。
四、实例化
抽象出来的类,可以通过具体的实例来实现特有的属性和方法,下面来演示实现类的实例化。
a = ClassName() # 实例化
使用上面的语法,就调用了类的实例化方法,完成实例化,创建了一个该类的对象(实例),例如:
tom = Person()
jerry = Person()
上面的tom、jerry都是类Person的实例,它们两个是不同的实例,即使使用同样的参数实例化,得到的也是不同的对象。
在实例化之后,会自动调用__init__方法进行初始化。
1、__init__方法
对实例进行初始化,MyClass()实际上调用的就是__init__(self)方法,可以不去定义,它会隐式去调用这个方法。演示如下:
class MyClass:
def __init__(self):
print("实例化之后调用init方法") MyClass() # 实例化之后调用init方法
init可以传递多个参数,注意第一个位置必须是self,并且不能有返回值。可以通过实例化后的对象去调用类中的属性:
class Person:
def __init__(self, name, age):
self.name = name # 将实例化传递进来的参数赋值给类属性
self.age = age def showage(self):
print("{} is {}".format(self.name, self.age)) # 通过self可以获取该类的属性 tom = Person("tom", 22) # 实例化
jerry = Person("jerry", 23) # 通过实例化后的对象可以调用其属性
print(tom.name, jerry.name) # tom jerry
jerry.age += 1
print(jerry.age) #
jerry.showage() # jerry is 24
2、实例变量和类变量
实例变量是每一个实例自己独有的,通过类名是访问不到的;类变量是所有实例共享的属性和方法,其实例都可以访问:
class Person:
age = 3 # 类变量 def __init__(self, name):
self.name = name # 实例变量 tom = Person("tom")
jerry = Person("jerry") print(tom.name, tom.age) # tom 3
print(jerry.name, jerry.age) # jerry 3
Person.age = 30
print(Person.age, tom.age, jerry.age) # 30 30 30
3、对象的特殊属性
每一个对象都拥有不同的属性,函数、类都是对象,类的实例也是对象。
特殊属性 | 含义 |
__name__ | 对象名 |
__class__ | 对象的类型 |
__dict__ | 对象的属性的字典 |
__qualname__ | 类的限定名 |
举例如下:
class Person:
age = 3 # 类变量 def __init__(self, name):
self.name = name # 实例变量 # 类的属性
print(Person.__class__.__name__) # type
print(sorted(Person.__dict__.items())) # [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x0000008C5F541840>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)] # 实例的属性
tom = Person("tom")
print(tom.__class__.__name__) # Person
print(sorted(tom.__dict__.items())) # [('name', 'tom')] # 通过实例访问类的属性
print(tom.__class__.__name__) # Person
print(sorted(tom.__class__.__dict__.items())) # [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x0000008C5F541840>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
实例属性的查找顺序:实例会先找自己的__dict__,如果没有然后通过属性__class__找到自己的类,再去类的__dict__中去找。
五、类装饰器
本质上是为类对象动态增加了一个属性,而Person这个标识符指向这个类对象。
def add_name(name):
def wrapper(cls):
cls.name = name
return cls return wrapper @add_name("tom")
class Person:
age = 3 print(Person.name) # tom
六、类方法和静态方法
静态方法,不需要实例。静态方法主要是用来存放逻辑性的代码,主要是一些逻辑属于类,但是和类本身没有交互,即在静态方法中,不会涉及到类中的方法和属性的操作。可以理解为将静态方法存在此类的名称空间中。
类方法是将类本身作为对象进行操作的方法。他和静态方法的区别在于:不管这个方式是从实例调用还是从类调用,它都用第一个参数把类传递过来。
class Person:
AGE = 20 @classmethod
def class_method(cls):
print("class method")
cls.AGE = 170 @staticmethod
def static_method():
print("static method") Person.class_method() # class method
Person.static_method() # static method
print(Person.__dict__) # {'__doc__': None, 'static_method': <staticmethod object at 0x0000008CC02BAEF0>, 'AGE': 170, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, 'class_method': <classmethod object at 0x0000008CC029D470>}
七、访问控制
1、私有变量
使用双下划线开头的属性名,就是私有属性。将属性设为私有,让外部访问不到,其本质就是将该属性名改名,改为(_类名__变量名),使用原来的属性名就访问不到了。那么如何去访问这个私有属性呢,可以通过方法来访问该私有属性。
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age def growup(self, i=1):
if i>0 and i<100:
self.__age += 1 # 私有属性通过方法去访问
def getage(self):
return self.__age tom = Person("tom")
tom.growup(20)
# print(tom.__age) # AttributeError: 'Person' object has no attribute '__age'
print(tom.__dict__) # {'name': 'tom', '_Person__age': 19}
print(tom.getage()) #
2、私有方法
私有方法的本质和私有变量的本质一样,只是将方法的名称改变(_类名__方法名)
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age # 保护方法,约定,没有改名
def _getage(self):
return self.__age # 私有方法,改名
def __getage(self):
return self.__age tom = Person("tom")
print(tom._getage()) #
# print(tom.__getage()) # AttributeError: 'Person' object has no attribute '__getage'
print(tom.__class__.__dict__) # {'__dict__': <attribute '__dict__' of 'Person' objects>, 'growup': <function Person.growup at 0x0000000935BC1A60>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, '__init__': <function Person.__init__ at 0x0000000935BC1840>, '_Person__getage': <function Person.__getage at 0x0000000935BC1AE8>, '__module__': '__main__'}
print(tom._Person__getage()) #
3、保护变量、方法
在变量或者方法名前面加上单下划线,就是保护变量、方法。这种变量或者方法可以直接访问,并没有改名,这只是开发者共同的约定,看到这种变量就如同私有变量,不要直接使用。
class Person:
def __init__(self, name, age=18):
self.name = name
self._age = age # 保护变量 # 保护方法,约定,没有改名
def _getage(self):
return self._age tom = Person("tom")
print(tom._age) #
print(tom._getage()) #
print(tom.__dict__) # {'_age': 18, 'name': 'tom'}
print(Person.__dict__) # {'__module__': '__main__', '__doc__': None, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__init__': <function Person.__init__ at 0x000000E3DB071840>, '_getage': <function Person._getage at 0x000000E3DB071A60>}
八、补丁
在运行时,对属性、方法、函数等进行动态替换。补丁往往是为了通过替换,修改来增强、扩展原有代码的能力。
# t1(原代码)
class Person:
def get_score(self):
ret = {"English": 78, "Chinese": 100, "History": 89}
return ret
# t2(打补丁)
from t_class.t1 import Person # 要修改的内容
def get_score(self):
return dict(name=self.__class__.__name__, English=88, Chinese=38, History=100) # 打补丁
def monkeypatchPerson():
Person.get_score = get_score if __name__ == '__main__':
monkeypatchPerson()
print(Person().get_score())
九、属性装饰器
一般好的设计是将实例的属性保护起来,不让外部去直接访问,外部使用getter和setter方法去获取和设置属性。
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age @property
def age(self):
return self.__age @age.setter
def age(self, age):
self.__age = age @age.deleter
def age(self):
del self.__age
print("del age") tom = Person("tom", 22)
print(tom.age) #
tom.age = 18
print(tom.age) #
del tom.age # del age
- 使用property装饰器的时候这三个方法同名
- property装饰器:它就是getter,这个必须有,有了它至少是只读属性
- setter装饰器:接收两个参数,第一个是self,第二个是要修改的值,有了它属性可写
- deleter装饰器:删除属性,很少用
(python)面向对象的更多相关文章
- python 面向对象初级篇
Python 面向对象(初级篇) 概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发" ...
- Python 面向对象 基础
编程范式概述:面向过程 和 面向对象 以及函数式编程 面向过程:(Procedure Oriented)是一种以事件为中心的编程思想. 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现 ...
- python面向对象进阶(八)
上一篇<Python 面向对象初级(七)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...
- python 面向对象(进阶篇)
上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...
- python 面向对象编程学习
1. 问题:将所有代码放入一个py文件:无法维护 方案:如果将代码才分放到多个py文件,好处: 1. 同一个名字的变量互相不影响 2.易于维护 3.引用模块: import module 2.包:解决 ...
- Python面向对象详解
Python面向对象的"怜人之处" Python的待客之道--谁能进来 Python的封装--只给你想要的 Python的继承--到处认干爹 Python的多态--说是就是
- python 面向对象和类成员和异常处理
python 面向对象 你把自己想象成一个上帝,你要创造一个星球,首先你要把它揉成一个个球,两个直径就能创造一个球 class star: '''名字(name),赤道直径(equatorial di ...
- python 面向对象学习
------Python面向对象初 下面写一个类的简单实用,以便方便理解类 #python 3.5环境,解释器在linux需要改变 #阅读手册查询readme文件 #作者:S12-陈金彭 class ...
- 初识python面向对象
一.初识python面向对象: class Person: #使用class关键字定义一个类 age=0 #类变量(静态变量) def eat(self,food): #定义一个方法 self.age ...
- python 面向对象、特殊方法与多范式、对象的属性及与其他语言的差异
1.python 面向对象 文章内容摘自:http://www.cnblogs.com/vamei/archive/2012/06/02/2532018.html 1.__init__() 创建对 ...
随机推荐
- vue android低版本 白屏问题 你是不是用了Object.assign ??
问题描述 在部分比较低版本的手机中,发现apk安装后白屏,但是大部分手机都能安装. 本人在使用android4.4时候,也是安装后打开白屏. 原因: 代码写法不兼容 this.user = Objec ...
- vue 项目实战 (vue全家桶之--- vuex)
老规矩先安装 npm install vuex --save 在看下面内容之前 你应该大概的看了一边vuex官方的文档对vuex有个大概对了解 首先 vuex 是什么? vuex 是属于vue中的什么 ...
- Win32线程安全问题.同步函数
线程安全问题.同步函数 一丶简介什么是线程安全 通过上面几讲.我们知道了线程怎么创建.线程切换的原理(CONTEXT结构) 每个线程在切换的时候都有自己的堆栈. 但是这样会有安全问题. 为什么? 我 ...
- 【转载】浅谈38K红外发射接受编码
转自Doctor_A 坛友的笔记! 之前做接触过一次红外遥控器,现在有空想用简单的话来聊一聊,下面有错误的地方欢迎改正指出: 1:红外的概念不聊,那是一种物理存在.以下聊38K红外发射接收,主要讲可编 ...
- 数据分析面试题之Pandas中的groupby
昨天晚上,笔者有幸参加了一场面试,有一个环节就是现场编程!题目如下: 示例数据如下,求每名学生(ID)对应的成绩(score)最高的那门科目(class)与ID,用Python实现: 这个题目 ...
- c# json 序列化时遇到错误 error Self referencing loop detected for type
参考网址:http://blog.csdn.net/adenfeng/article/details/41622255 在写redis缓存帮助类的时候遇到的这个问题,本来打算先序列化一个实体为json ...
- 微服务框架Lagom介绍之一
背景 Lagom是JAVA系下响应式 微服务框架,在阅读本文之前请先阅读微服务架构设计,Lagom与其他微服务框架相比,与众不同的特性包括: 目前,大多数已有的微服务框架关注于简化单个微服务的构建-- ...
- Python多线程的简单实现(生产者消费者模型)
__author__ = "JentZhang" import time, threading, queue q = queue.Queue(maxsize=) # 声明队列 de ...
- element UI Cascader 级联选择器 编辑 修改 数组 路径 问题(转载)
来源:https://segmentfault.com/a/1190000014827485 element UI的Cascader级联选择器编辑时 vue.js element-ui 2 eleme ...
- C# winform程序怎么打包成安装项目(VS2010图解)
作为研发人员,在本机上开发的winform.wpf或者控制台程序需要发给其他人测试时候,一般需要对其进行打包生成setup安装文件,根据网上查找的资料并结合自己打包成功,记录如下: 注:本程序是一个利 ...