Python 中一切皆对象

什么是面向对象?

面向对象就是将一些事物的共有特征抽象成类,从类来创建实例。

类class

可以理解为模版

比如人类,都具有身高、体重、年龄、性别、籍贯。。。等属性,但属性的值都不尽相同。

对象instance、object

对象是类抽象出来的一个实例。比如小鸣同学和小红同学,他们都是人类这个类抽象出来的一个人,但小鸣是男同学,小红是女同学,他们都各自的身高、体重。。。等等属性。

属性

属性是对象状态的抽象,用数据结构来描述。

操作(动作、方法)

操作是对象行为的抽象,用操作名和实现该操作名的方法来描述

面向对象三要素

1. 封装

组装:将数据和操作组装到一起

隐藏数据:对外只暴露一些接口,通过接口访问对象。(隐藏细节)

2. 继承:

代码复用:继承来的就不用自己写了

3. 多态:

接口重用,一个接口,多种形态。

举例:比如每个动物都有吃这个动作,如果让狗、猫、鸡去吃,那就得分别调用狗的吃方法、猫的吃方法、鸡的吃方法。虽然调用了3次都做了吃这个动作,但如果有100个动物呢?那就可以使用多态的特性了,我可以封装一个接口,接口的作用是接受传入的动物名去执行吃这个动作,这样就可以理解成只需要调用这个接口,分别传入狗、猫、鸡,就可以执行吃这个动作了。

如何定义类:

class ClassName:
语句块

  1. 必须使用class关键字

  2. 类名必须是大驼峰方式命名

  3. 类定义完成后,就产生了一个类对象,绑定到了ClassName上

实例化:

instancename = ClassName()

构造函数:

实例创建时类的初始化函数,初始化对象的一些属性,这些属性会bound到对象上。

类变量:

类的变量是类的所有实例可以共享的一些变量,比如都是在学习Pyhon课程,那么这些课程这个变量就可以定义为类的变量而不用每次都单独定义一个同样的课程变量。

实例变量:

假如学生是一个实例,那么每个学生的名字、年龄、身高、体重等各自的特征就是实例本身的变量,教室地点、课程名这两个所有学生都共有的变量就可以定义成类变量。

举例:

class MyClass:
'''a example class'''
address = 'ShanXi' #类变量
course = 'Python' def __init__(self, name, age, length=170, width=110): #构造函数,初始化函数
self.name = name #实例变量
self.age = age
self.length = length
self.width = width def info(self):
print(self.name, self.age, self.address, self.course, self.length, self.width) print('MyClass: ', MyClass.__dict__) s1 = MyClass('zhangsan', '18') #实例1
print(s1.__dict__)
print(s1.name, s1.age, s1.address, s1.course, s1.length, s1.width) s2 = MyClass('lisi', '23', width=130) #实例2
print(s2.__dict__)
print(s2.name, s2.age, s2.address, s2.course, s2.length, s2.width)

  

  输出结果:

MyClass:  {'__module__': '__main__', 'course': 'Python', '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__doc__': 'a example class', 'info': <function MyClass.info at 0x100774ae8>, '__init__': <function MyClass.__init__ at 0x100774a60>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, 'address': 'ShanXi'}
{'width': 110, 'length': 170, 'age': '18', 'name': 'zhangsan'}
zhangsan 18 ShanXi Python 170 110
{'width': 130, 'length': 170, 'age': '23', 'name': 'lisi'}
lisi 23 ShanXi Python 170 130

  

在实例化一个类以后,都会自动调用一个__init__方法,方法的第一个参数只能是self,表示接受的参数是实例本身。

__init__方法可以有多个行参,可以使用位置参数、关键字参数。

__init__方法不能有返回值,也就是只能为None。

通过__dict__特殊方法,可以查看类或者实例各自的变量:

类的__dict__方法,保存的是所有实例共有的变量

实例的__dict__方法,保存的是实例自身的变量。

所以实例的变量有个查找顺序,只有在实例没有这个变量时才会去类的变量中查找,如果类中有就返回。

如果实例定义了一个与类变量同名的变量,那实例会新增这个变量,覆盖掉类的变量。

比如: s1 和 s2 两个实例本身并没有变量address、course,但类里面有,所以上面例子中仍然可以通过实例的address、course变量。

如果实例 s1 定义了与类变量同名的变量 address,那么s1 的这个私有变量就会覆盖掉类的变量,示例:

class MyClass:
'''a example class'''
address = 'ShanXi'
course = 'Python' def __init__(self, name, age, length=170, width=110):
self.name = name
self.age = age
self.length = length
self.width = width s1 = MyClass('zhangsan', '18')
print(s1.name, s1.age, s1.address, s1.course) s1.address = 'ShangHai' #实例变量赋值
print(s1.name, s1.age, s1.address, s1.course) s2 = MyClass('lisi', '23', width=130)
print(s2.name, s2.age, s2.address, s2.course)

  返回结果:

zhangsan 18 ShanXi Python
zhangsan 18 ShangHai Python
lisi 23 ShanXi Python

  可以看到实例s1 的address 变量赋值为"ShangHai" 后,覆盖了类的值"ShanXi",但是并没有影响到实例s2 的address值(s1.__dict__)。

一般来说,类的变量名应该使用全大写来命名。

装饰一个类

装饰器函数是对普通函数的功能增强。装饰器函数也是一个函数,可以接收一个函数作为行参,在内部对函数功能做修饰后,返回函数结果。

需求:为一个类通过装饰,增加一些类属性

# 增加类变量
# def add_name(name, clz):
# clz.NAME = name # 改进成装饰器
def add_name(name):
def wrapper(clz):
clz.NAME = name
return clz
return wrapper @add_name('Jack')
class MyClass:
AGE = 30 print(MyClass.NAME)

  

特殊方法    含义

__name__  对象名

__class__  对象的类型

__dict__    对象的属性的字典

__qualname__  类的限定名

类方法和静态方法

class Person:
ADDRESS = 'BeiJIng'
COURSE = 'Python' def __init__(self, name, age):
self.name = name
self.age = age def normal_method():
print('normal') def method(self):
print('{} is method'.format(self)) @classmethod
def class_method(cls):
print('class = {0} ({0.COURSE})'.format(cls))
# print('instance attribute name = {}'.format(cls.name))
cls.HEIGHT = 170 @staticmethod
def static_method():
print('static method') print('分别测试类和实例是否可以访问以上4种方法')
print('类访问~~~~~~~~~~~~')
print(1, Person.normal_method()) #类访问普通方法,可以
# print(2, Person().normal_method()) #等同于实例访问普通方法,不可以
# print(3, Person.method()) #类访问实例方法,不可以
print(4, Person.class_method()) #类访问类方法,可以
print(5, Person.static_method()) #类访问静态方法,可以 print('实例访问~~~~~~~~~~~~')
s1 = Person('jack', 20) #实例化
# print(1, s1.normal_method()) #实例访问普通方法,不可以
print(2, s1.method()) #实例访问实例方法,可以
print(3, s1.class_method()) #实例访问类方法,可以,但是只能访问类变量
print(4, s1.static_method()) #实例访问静态方法,可以

normal_method普通方法    不接收参数,类可以调用,但实例不可以调用,不可以访问类变量和实例变量
实例方法                         实例方法,指类中可以接收self参数的函数。既可以访问类变量,也可以访问实例变量
staticmethod                   静态方法,就是类里面的普通函数,但实例可以调用。 静态方法装饰的函数不可以访问类变量和实例变量
classmethod                    类方法,只能访问类变量,不能访问实例的变量

  总结:

类方法几乎可以调用所有内部定义的方法,但是调用实例方法时会报错,原因是第一参数必须是类的实例。

实例也几乎可以调用所有的方法,但是不可以调用普通方法,因为实例调用时会隐式传入实例,但普通方法根本不接收参数。

类可以调用:普通方法,类方法,静态方法

实例可以调用:实例方法,类方法,静态方法

私有(Private)属性

举例:

class Person:
def __init__(self, name, age=18):
self.name = name
self.age = age def growup(self, i=1):
if i > 0 and i < 150:
print(self.age)
self.age += i
print(self.age) p1 = Person('tom')
p1.growup(10)
print(p1.__dict__) p1.age = 150
p1.growup(20)
print(p1.__dict__)

  

上面的例子中,本来是想通过方法控制属性,但是由于属性在外部可以访问,或者说可见,就可以直接绕过方法,直接修改这个属性。

Python提供了私有属性可以解决这个问题

私有属性

使用双下划线开头的属性名,就是私有属性

class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age def growup(self, i=1):
if i > 0 and i < 150:
print(self.__age)
self.__age += i
print(self.__age) p1 = Person('tom')
p1.growup(10)
print(p1.__dict__)
# print(p1.__age) #外部访问不到

  输出结果会报错:

18
28
{'_Person__age': 28, 'name': 'tom'} Traceback (most recent call last):
File "/Users/zhangsan/Desktop/python/1106面向对象/Private_test1.py", line 15, in <module>
print(p1.__age) #外部访问不到
AttributeError: 'Person' object has no attribute '__age'

 通过上面这个例子可以看出,外部已经无法访问__age变量了,在__dict__中也可以看到age被改名成了_Person__age。

 

可以在类中通过方法来访问这个私有变量__age,也可以实例化之后调用这个接口访问。

class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age def growup(self, i=1):
if i > 0 and i < 150:
self.__age += i def getage(self):
return self.__age p1 = Person('tom')
p1.growup(10)
print(p1.getage())

  输出结果:

28

在为实例动态添加一个__age变量时,不会覆盖掉原来的__age。

class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age def growup(self, i=1):
if i > 0 and i < 150:
# print(self.__age)
self.__age += i
# print(self.__age) def getage(self):
return self.__age p1 = Person('tom')
p1.growup(10)
# print(p1.__age) #访问不到
print(p1.getage())
print(p1.__dict__) p1.__age = 150
print(p1.__age) # 150,没有覆盖__age变量
print(p1.getage())
print(p1.__dict__)

  输出结果:

28
{'name': 'tom', '_Person__age': 28} #没有__age变量,但有一个很奇怪的变量,稍后介绍 150
28
{'name': 'tom', '__age': 150, '_Person__age': 28}

原因是变量有查找顺序,也就是先找实例变量-再找类变量,会发现实例和类都没有__age这个变量,所以创建了一个__age变量,这个变量属于实例变量,所以等同于重新为一个同名变量赋值而已。

实例__dict__属性,保存的是关于实例自身的一些变量

类的__dict__属性,保存是所有实例共享的一些变量。

类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名,转换名称为 _类名__变量名 的名称,所以用原来的名字访问不到了。但是如果外部知道了修改之后的名字,在外部也可以直接访问或修改。可见python变量并没有绝对的保护起来。

class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age def growup(self, i=1):
if i > 0 and i < 150:
# print(self.__age)
self.__age += i
# print(self.__age) def getage(self):
return self.__age p1 = Person('tom')
p1.growup(10)
# print(p1.__age) #访问不到
print(p1.getage())
print(p1.__dict__) p1.__age = 150
print(p1.__age) #没有覆盖__age变量
print(p1.getage())
print(p1.__dict__) #直接修改私有变量
p1._Person__age = 80
print(p1.getage()) #80 可以直接修改
print(p1.__dict__)

  输出结果:

28
{'_Person__age': 28, 'name': 'tom'}
150
28
{'_Person__age': 28, '__age': 150, 'name': 'tom'}
80
{'_Person__age': 80, '__age': 150, 'name': 'tom'}

  

保护变量

在变量名前使用一个下划线,称为保护变量。

class Person:
def __init__(self, name, age=18):
self.name = name
self._age = age p1 = Person('jack')
print(p1._age)
#p1._age = 22 #不要直接使用
print(p1.__dict__)

  输出结果:

18
{'_age': 18, 'name': 'jack'}

  这个_age属性根本就没有改变名称,和普通的属性一样,解释器不做任何特殊处理。

这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用。

私有方法

参照私有变量、保护变量,使用单下划线、双下划线命名方法。

class Person:
def __init__(self, name, age=18):
self.name = name
self._age = age def _getname(self):
return self.name def __getage(self):
return self._age p1 = Person('jack')
print(p1._getname())
# print(p1.__getage()) #无法访问此属性,被改名为'_Person__getage'
print(p1._age)
print(p1.__dict__)
print(p1.__class__.__dict__) #
print(p1._Person__getage())

  

补丁

想象这样一个场景:

我们公司内部维护有一套系统,最近有发现这套系统有个bug,但临时又不想立刻修复并推到线上,而是想等过两天版本大升级再统一将修复后的程序合并发布。

这样的情况下,就可以使用猴子补丁(monkey patch),在运行时对属性进行动态修改以修复bug。

1. 程序运行文件   test1.py

将调用到了bug模块的方法替换成补丁文件中的方法

from test2 import Person
from test3 import get_score def monkeypatch4Person():
Person.get_score = get_score monkeypatch4Person() if __name__ == "__main__":
print(Person().get_score())

  

2. 被调用的模块  test2.py

模拟从数据库中读取数据

class Person:
def get_score(self):
# connect to mysql
ret = {'English':78, 'Chinese':86, 'History':82}
return ret

  

3. 补丁文件       test3.py

修改数据,模拟修复

def get_score(self):
return dict(name=self.__class__.__name__, English=88, Chinese=90, History=85)

  

继承

py2经典类深度优先  新式类广度优先

py3中都是新式类,广度优先

封装  将数据和操作(即属性和方法)组合起来,对外隐藏细节,只暴露接口供调用

继承  代码重用

多态  一个接口多种形态,接口重用

子类,也叫派生类,继承类

父类,也叫基类

多态

多态的前提:继承,覆盖

多继承

装饰器装饰类添加功能:

def printable(cls):
def prt(self):
return self.content
cls.print = prt
# cls.print = lambda self:self.content
return cls @printable
class Printabel:
def __init__(self, content):
self.content = content print(Printabel.__dict__) a = Printabel('abc')
print(a.print())

  输出结果:

{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Printabel' objects>, '__doc__': None, '__init__': <function Printabel.__init__ at 0x000001792E6B2BF8>, 'print': <function printable.<locals>.prt at 0x000001792E6B2840>, '__weakref__': <attribute '__weakref__' of 'Printabel' objects>}
abc

  

Mixin

利用多继承,将其它类混入进入,本质上是修改MRO顺序。不要在Mixin类中添加__init__方法,以免覆盖初始化

class PrintableMixin:
def prt(self):
print('Mixin: {}'.format(self.content)) class Document:
def __init__(self,content):
self.content = content def prt(self):
print(self.content) class Word(Document):
def prt(self):
print('Word prt: {}'.format(self.content)) class Pdf(Document): pass class PrintablePdf(PrintableMixin,Pdf): #混入的类尽量靠前,覆盖
pass print(PrintablePdf.mro())
word = PrintablePdf('python')
word.prt()

  输出结果:

[<class '__main__.PrintablePdf'>, <class '__main__.PrintableMixin'>, <class '__main__.Pdf'>, <class '__main__.Document'>, <class 'object'>]
Mixin: python

  

Python 面向对象(一) 基础的更多相关文章

  1. Python 面向对象编程基础

    Python 面向对象编程基础 虽然Pthon是解释性语言,但是Pthon可以进行面向对象开发,小到 脚本程序,大到3D游戏,Python都可以做到. 一类: 语法: class 类名: 类属性,方法 ...

  2. Python 面向对象之五 基础拾遗

    Python 面向对象之五 基础拾遗 今天呢,就剩下的面向对象的相关知识进行学习,主要会学习以下几个方面的知识:1.上下文管理协议,2.为类加装饰器 3.元类 一.上下文管理协议 在学习文件操作的时候 ...

  3. python面向对象的基础语法(dir内置函数、self参数、初始化方法、内置方法和属性)

    面相对象基础语法 目标 dir 内置函数 定义简单的类(只包含方法) 方法中的 self 参数 初始化方法 内置方法和属性 01. dir 内置函数(知道) 在 Python 中 对象几乎是无所不在的 ...

  4. python面向对象编程基础

    演示了 Python 类与对象的编程基础, 包括属性.方法.继承.组合.动态创建类. python 版本: 2.7.5 class SimpleClass(object): ''' a simple ...

  5. python——面向对象(1),基础

    """面向对象:抽象化编程思想.类,对象:用类来创建(实例化)对象.类:一系列特征和行为相同的事物总和, 1.属性:特征 2.行为:方法 定义类 :PEP 8要求标识符的 ...

  6. Python 面向对象 基础

    编程范式概述:面向过程 和 面向对象 以及函数式编程 面向过程:(Procedure Oriented)是一种以事件为中心的编程思想. 就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现 ...

  7. python学习第十四天 -面向对象编程基础

    python也是支持面向对象编程的.这一章节主要讲一些python面向对象编程的一些基础. 什么是面向对象的编程? 1.面向对象编程是一种程序设计范式 2.把程序看做不同对象的相互调用 3.对现实世界 ...

  8. 二十. Python基础(20)--面向对象的基础

    二十. Python基础(20)--面向对象的基础 1 ● 类/对象/实例化 类:具有相同属性.和方法的一类人/事/物 对象(实例): 具体的某一个人/事/物 实例化: 用类创建对象的过程→类名(参数 ...

  9. Python基础 — 面向对象编程基础

    目录 1. 面向对象编程基础 2. 定义类和创建对象 3. init() 方法 4. 魔法方法 5. 访问可见性问题 5. 练习 1. 面向对象编程基础 把一组数据结构和处理它们的方法组成对象(obj ...

随机推荐

  1. 提纲挈领webrtc之NS(noise suppression)模块

    Noise suppression,就是大家说的降噪.这种降噪是把人声和非人声区分开来,把非人声当成噪声. 一段包含人声和噪声的音频经过该模块处理,从理论上讲,只剩下人声了. webrtc的NS在业内 ...

  2. SELECT与SET对变量赋值

    SQL Server 中对已经定义的变量赋值的方式用两种,分别是 SET 和 SELECT. 对于这两种方式的区别,SQL Server 联机丛书中已经有详细的说明,但很多时候我们并没有注意,其实这两 ...

  3. PHP之回调函数传参(解决eval函数拼接对象参数的问题)

    在使用Smarty时,定义了一个统一调用控制器的函数,如下: function C($name, $method){//控制器的名称和其中方法的名称 require_once "contro ...

  4. TsBatis 预览

    前言 在发布了 mybatis-dynamic-query之后感觉基本功能稳定了,而且现在在工作项目开发效率大大提高,而且非常易于维护. 最近准备带几个小朋友以前用typescript 打通前后端,当 ...

  5. [extjs(2)] extjs第一个组件treepanel

    刚刚在接触extjs这个前段插件,由于公司是用这个来做前段的,所以有必要花点时间来掌握一下,下面是我自己的非常浅的学习总结,后期会慢慢添加的!! 一.TreePanel基本配置参数: animate: ...

  6. angularJS的环境搭建--初学

    一  \在这里简单介绍一下Angular-cli的特性: Angular-cli可以快速搭建框架,创建module,service,class,directive等: 有webpack的功能,可以实现 ...

  7. Memcached统计命令

    1. Memcached stats命令: Memcached stats 命令用于返回统计信息例如 PID(进程号).版本号.连接数等. 语法: stats 输出信息说明: pid: memcach ...

  8. LeetCode 605. Can Place Flowers (可以种花)

    Suppose you have a long flowerbed in which some of the plots are planted and some are not. However, ...

  9. 算法改进 | java语言中判断素数

    参考文章:http://blog.csdn.net/kp_liu/article/details/37569507 http://blog.csdn.net/huang_miao_xin/articl ...

  10. [转]Java - 集合框架完全解析

    数据结构是以某种形式将数据组织在一起的集合,它不仅存储数据,还支持访问和处理数据的操作.Java提供了几个能有效地组织和操作数据的数据结构,这些数据结构通常称为Java集合框架.在平常的学习开发中,灵 ...