10-python程序员,面向对象基础

《python小白入门系列教程》html

有对象吗?
python

没有就new 一个git

今天咱们要用python new 一个对象程序员

面向过程VS面向对象

1)面向过程

  核心是过程(流水线式思惟),过程即解决问题的步骤,面向过程的设计就比如精心设计好一条流水线,考虑周全何时处理什么东西。编程

  优势是:极大的下降了写程序的复杂度,只须要顺着要执行的步骤,堆叠代码便可。函数

  缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。oop

  应用场景:一旦完成基本不多改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。学习

2)面向对象

  核心是对象(上帝式思惟),==要理解对象为什么物,必须把本身当成上帝==,上帝眼里世间存在的万物皆为对象,不存在的也能够创造出来。this

面向对象的程序设计比如如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题须要四我的:唐僧,沙和尚,猪八戒,孙悟空,每一个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法)lua

然而这并很差玩,因而如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。而后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。

优势是:解决了程序的扩展性。对某一个对象单独修改,会马上反映到整个体系中,如对游戏中一我的物参数的特征和技能修改都很容易。

缺点:可控性差,没法向面向过程的程序设计流水线式的能够很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即使是上帝也没法预测最终结果。因而咱们常常看到一个游戏人某一参数的修改极有可能致使阴霸的技能出现,一刀砍死3我的,这个游戏就失去平衡。

  应用场景:需求常常变化的软件,通常需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

相关名词概念

:具备相同特征的一类事物(人、狗、老虎,机器人)

对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)

实例化:类——>对象的过程

==开始一本正经的写教程==

类与对象是面向对象编程的两个主要方面。一个类(Class)可以建立一种新的类型 (Type),其中对象(Object)就是类的实例(Instance)。能够这样来类比:你能够拥有 类型 int 的变量,也就是说存储整数的变量是 int 类的实例(对象)。

对象可使用属于它的普通变量来存储数据。这种从属于对象或类的变量叫做字段 (Field)。对象还可使用属于类的函数来实现某些功能,这种函数叫做类的方法 (Method)。这两个术语很重要,它有助于咱们区分函数与变量,哪些是独立的,哪些又是 属于类或对象的。总之,字段与方法通称类的属性(Attribute)

字段有两种类型——它们属于某一类的各个实例或对象,或是从属于某一类自己。它们被分 别称做实例变量(Instance Variables)与类变量(Class Variables`)。

经过 class 关键字能够建立一个类。这个类的字段与方法能够在缩进代码块中予以列出。

self

类方法与普通函数只有一种特定的区别——前者必须多加一个参数在参数列表开头,这个名 字必须添加到参数列表的开头,可是你不用在你调用这个功能时为这个参数赋值,Python 会 为它提供。这种特定的变量引用的是对象自己,按照惯例,它被赋予 self 这一名称

尽管你能够为这一参数赋予任何名称,可是强烈推荐你使用 self 这一名称——其它的任何 一种名称绝对会引人皱眉。使用一个标准名称能带来诸多好处——任何一位你的程序的读者 可以当即认出它,甚至是专门的 IDE(Integrated Development Environments,集成开发环 境)也能够为你提供帮助,只要你使用了 self 这一名称。

针对 C++/Java/C# 程序员的提示 Python 中的 self 至关于 C++ 中的 this 指针以及 Java 与 C# 中的 this 引用。

你必定会在想 Python 是如何给 self 赋值的,以及为何你没必要给它一个值。一个例子或许 会让这些疑问获得解答。假设你有一个 MyClass 的类,这个类下有一个实例 myobject 。当 你调用一个这个对象的方法,如 myobject.method(arg1, arg2) 时,Python 将会自动将其转 换成 MyClass.method(myobject, arg1, arg2) ——这就是 self 的所有特殊之处所在。

这同时意味着,若是你有一个没有参数的方法,你依旧必须拥有一个参数—— self 。

最简单的类(Class)能够经过下面的案例来展现(保存为 oop_simplestclass.py ):

class Person
    : pass # 一个空的代码块 

p = Person() 
print(p)

输出:

python oop_simplestclass.py 
<__main__.Person instance at 0x10171f518>
它是如何工做的

咱们经过使用 class 语句与这个类的名称来建立一个新类。在它以后是一个缩进的语句块, 表明这个类的主体。在本案例中,咱们建立的是一个空代码块,使用 pass 语句予以标明。

而后,咱们经过采用类的名称后跟一对括号的方法,给这个类建立一个对象。为了验证咱们的操做是否成功,咱们经过 直接将它们打印出来来确认变量的类型。结果告诉咱们咱们在 Person 类的 __main__ 模块 中拥有了一个实例。

要注意到在本例中还会打印出计算机内存中存储你的对象的地址。案例中给出的地址会与你 在你的电脑上所能看见的地址不相同,由于 Python 会在它找到的任何空间来存储对象。

方法

咱们已经在前面讨论过类与对象一如函数那般均可以带有方法(Method),惟一的不一样在于 咱们还拥有一个额外的 self 变量。如今让咱们来看看下面的例子(保存为 oop_method.py )。

class Person: 
    def say_hi(self): 
        print('Hello, 你们好?') 

p = Person() 
p.say_hi() 
# 前面两行一样能够写做 
# Person().say_hi()

输出:

python oop_method.py 
 Hello, 你们好?
它是如何工做的

这里咱们就能看见 self 是如何行动的了。要注意到 say_hi 这一方法不须要参数,可是依 旧在函数定义中拥有 self 变量

__init__ 方法

在 Python 的类中,有很多方法的名称具备着特殊的意义。如今咱们要了解的就是 __init__ 方法的意义。

__init__ 方法会在类的对象被实例化 时当即运行。这一方法能够对任何你想 进行操做的目标对象进行初始化 操做。==这里你要注意在 init 先后加上的双下 划线==。

案例(保存为 oop_init.py ):

class Person: 
    def __init__(self, name): 
        self.name = name 
    def say_hi(self):
        print('Hello, 个人名字是', self.name) 
        
p = Person('木木') 
p.say_hi() 
# 前面两行同时也能写做 
# Person('Swaroop').say_hi()

输出:

python oop_init.py 
 Hello, 个人名字是木木
它是如何工做的

在本例中,咱们定义一个接受 name 参数(固然还有 self 参数)的 __init__ 方法。在这 里,咱们建立了一个字段,一样称为 name 。要注意到尽管它们的名字都是“name”,但这是 两个不相同的变量。虽然说如此,但这并不会形成任何问题,由于 self.name 中的点号意味着 这个叫做“name”的东西是某个叫做“self”的对象的一部分,而另外一个 name 则是一个局部变 量。因为咱们已经如上这般明确指出了咱们所指的是哪个名字,因此它不会引起混乱。

当咱们在 Person 类下建立新的实例 p 时,咱们采用的方法是先写下类的名称,后跟括在 括号中的参数,形如:p = Person('木木')

咱们不会显式地调用 __init__ 方法。这正是这个方法的特殊之处所在。

如今,咱们可使用咱们方法中的 self.name 字段了,使用的方法在 say_hi 方法中已经做 过说明。

类变量与对象变量

咱们已经讨论过了类与对象的功能部分(即方法),如今让咱们来学习它们的数据部分。数 据部分——也就是字段——只不过是绑定到类与对象的命名空间 的普通变量。这就表明着这些名称仅在这些类与对象所存在的上下文中有效。这就是它们被 称做“命名空间”的缘由。

字段(Field)有两种类型——类变量与对象变量,它们根据到底是类仍是对象拥有这些变量 来进行分类。

类变量(Class Variable)是共享的(Shared)——它们能够被属于该类的全部实例访问。该类变量只拥有一个副本,当任何一个对象对类变量做出改变时,发生的变更将在其它全部 实例中都会获得体现

对象变量(Object variable)由类的每个独立的对象或实例所拥有。在这种状况下,每一个 对象都拥有属于它本身的字段的副本,也就是说,它们不会被共享,也不会以任何方式与其 它不一样实例中的相同名称的字段产生关联。下面一个例子能够帮助你理解(保存为 oop_objvar.py ):

# coding=UTF-8 
class Robot: 
    """表示有一个带有名字的机器人。"""
    # 一个类变量,用来计数机器人的数量 
    population = 0
    def __init__(self, name): 
        """初始化数据""" 
        self.name = name 
        print("初始化 {})".format(self.name)) 

        # 当有人被建立时,机器人 
        # 将会增长人口数量 
        Robot.population += 1
    def die(self): 
        """我挂了。""" 
        print("{} 被销毁了!".format(self.name)) 
        
        Robot.population -= 1 
        
        if Robot.population == 0: 
            print("{} 是最后一个机器人".format(self.name)) 
        else:
            print("还有 {:d} 个机器人在工做.".format( Robot.population))
            
    def say_hi(self): 
        """来自机器人的诚挚问候 
        没问题,你作获得。""" 
        print("你们好,个人主人叫我{}.".format(self.name))
    
    @classmethod 
    def how_many(cls): 
        """打印出当前的机器人,人口数量""" 
        print("如今还有{:d} 个机器人.".format(cls.population))

droid1 = Robot("R2-D2") 
droid1.say_hi() 
Robot.how_many()

droid2 = Robot("C-3PO") 
droid2.say_hi() 
Robot.how_many()

print("\n机器人正在努力工做.\n")

print("机器人已经完成它的使命咱们要销毁它.") 
droid1.die() 
droid2.die()

Robot.how_many()

输出

python oop_objvar.py 
(初始化 R2-D2) 
你们好,个人主人叫我 R2-D2. 
还有 1个机器人在工做. 

(初始化 C-3PO) 
你们好,个人主人叫我 C-3PO. 
还有  2 个机器人在工做.  

机器人正在努力工做. 

机器人已经完成它的使命咱们要销毁它.

R2-D2 被销毁了! 
还有 1 个机器人在工做.
C-3PO 被销毁了! 
C-3PO 是最后一个机器人
还有0个机器人在工做.

它是如何工做的

这是一个比较长的案例,可是它有助于展示类与对象变量的本质。在本例中, population 属 于 Robot 类,所以它是一个类变量。name 变量属于一个对象(经过使用 self 分配),因 此它是一个对象变量。

所以,咱们经过 Robot.population 而非 self.population 引用 population 类变量。咱们对 于 name 对象变量采用 self.name 标记法加以称呼,这是这个对象中所具备的方法。要记住 这个类变量与对象变量之间的简单区别。同时你还要注意当一个对象变量与一个类变量名称 相同时,类变量将会被隐藏。

除了 Robot.popluation ,咱们还可使用 self.__class__.population ,由于每一个对象都经过 self.__class__ 属性来引用它的类。

how_many 其实是一个属于类而非属于对象的方法。这就意味着咱们能够将它定义为一个 classmethod(类方法) 或是一个 staticmethod(静态方法) ,这取决于咱们是否须要知道这一方 法属于哪一个类。因为咱们已经引用了一个类变量,所以咱们使用 classmethod(类方法) 。

咱们使用装饰器(Decorator)将 how_many 方法标记为类方法。

你能够将装饰器想象为调用一个包装器函数的快捷方式,所以启用

@classmethod 装饰器等价于调用:

how_many = classmethod(how_many)

你会观察到 __init__ 方法会使用一个名字以初始化 Robot 实例。在这一方法中,咱们将 population 按 1 往上增加,由于咱们多增长了一台机器人。你还会观察到 self.name 的值 是指定给每一个对象的,这体现了对象变量的本质。

你须要记住你只能使用 self 来引用同一对象的变量与方法。这被称做属性引用。

在本程序中,咱们还会看见针对类和方法的 文档字符串(DocStrings) 的使用方式。咱们可 以在运行时经过 Robot.__doc__ 访问类的 文档字符串,对于方法的文档字符串,则可使用 Robot.say_hi.__doc__ 。

在 die 方法中,咱们简单地将 Robot.population 的计数按 1 向下减小。

全部的类成员都是公开的。但有一个例外:若是你使用数据成员并在其名字中使用双下划线 做为前缀,造成诸如 __privatevar 这样的形式,Python 会使用名称调整使其有效地成为一个私有变量。

所以,你须要遵循这样的约定:任何在类或对象之中使用的变量其命名应如下划线开头,其 它全部非此格式的名称都将是公开的,并能够为其它任何类或对象所使用。请记得这只是一 个约定,==Python 并不强制如此(除了双下划线前缀这点)==。

全部类成员(包括数据成员)都是公开的,而且 Python 中全部的方法都是虚拟的 (Virtual)。

继承

面向对象编程的一大优势是对代码的重用,重用的一种实现方法就是经过继承 机制。继承最好是想象成在类之间实现类型与子类型

继承是一种建立新类的方式,在python中,新建的类能够继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类

如今假设你但愿编写一款程序来追踪一所大学里的老师和学生。有一些特征是他们都具备 的,例如姓名、年龄和地址。另一些特征是他们独有的,一如教师的薪水、课程与假期, 学生的成绩和学费。

你能够为每一种类型建立两个独立的类,并对它们进行处理。但增添一条共有特征就意味着 将其添加进两个独立的类。这很快就会使程序变得笨重。

一个更好的方法是建立一个公共类叫做 SchoolMember ,而后让教师和学生从这个类中继承 ,也就是说他们将成为这一类的子类型,而咱们就能够向这些子类型中添 加某些该类独有的特征。

同时还须要注意的是咱们重用父类的代码,但咱们不须要再在其它类中重复它们,当咱们使 用独立类型时才会必要地重复这些代码。

在上文设想的状况中, SchoolMember 类会被称做基类(Base Class)或是超类 (Superclass)。Teacher 和 Student 类会被称做派生类或是子类 (Subclass)。

咱们将经过下面的程序做为案例来进行了解(保存为 oop_subclass.py ):

# coding=UTF-8 
class SchoolMember: 
    '''表明任何学校里的成员。''' 
    def __init__(self, name, age): 
        self.name = name 
        self.age = age 
        print('(初始化学校成员: {})'.format(self.name)) 
        
    def tell(self): 
        '''告诉我有关个人细节。''' 
        print('姓名:"{}" 年龄:"{}"'.format(self.name, self.age), end=" ") 
      
        
class Teacher(SchoolMember): 
     '''表明一位老师。''' 
    def __init__(self, name, age, salary): 
        SchoolMember.__init__(self, name, age) 
        self.salary = salary print('(初始化教师: {})'.format(self.name)) 
        
    def tell(self): 
        SchoolMember.tell(self) 
        print('薪资: "{:d}"'.format(self.salary)) 
        
        
class Student(SchoolMember): 
    '''表明一位学生。'''
    def __init__(self, name, age, marks): 
        SchoolMember.__init__(self, name, age) 
        self.marks = marks
        print('(初始化学生: {})'.format(self.name)) 
                
    def tell(self): 
        SchoolMember.tell(self) 
        print('分数: "{:d}"'.format(self.marks)) 
        
t = Teacher('曾老师', 30, 30000) 
s = Student('小明', 20, 75) 

# 打印一行空白行 
print() 

members = [t, s] 
for member in members: 
    # 对全体师生工做 
    member.tell()

输出:

python oop_subclass.py 
(初始化学校成员: 曾老师) 
(初始化教师: 曾老师) 
(初始化学校成员: 小明) 
(初始化学生: 小明) 

姓名:"曾老师" 年龄:"30" 薪资: "30000" 
姓名:"小明" Age:"20" 分数: "75"

它是如何工做的

要想使用继承,在定义类 时咱们须要在类后面跟一个包含基类名称的元组。

而后,咱们会注 意到基类的 __init__ 方法是经过 self 变量被显式调用的,所以咱们能够初始化对象的基 类部分。

下面这一点很重要,须要牢记——由于咱们在 Teacher 和 Student 子类中定义了 __init__ 方法,Python 不会自动调用基类 SchoolMember 的构造函数,你必须本身显式地 调用它。

相反,若是咱们没有在一个子类中定义一个 __init__ 方法,Python 将会自动调用基类的构 造函数。

咱们会观察到,咱们能够经过在方法名前面加上基类名做为前缀,再传入 self 和其他变 量,来调用基类的方法。

在这里你须要注意,当咱们使用 SchoolMember 类的 tell 方法时,咱们能够将 Teacher 或 Student 的实例看做 SchoolMember 的实例。

同时,你会发现被调用的是子类型的 tell 方法,而不是 SchoolMember 的 tell 方法。理 解这一问题的一种思路是 Python 总会从当前的实际类型中开始寻找方法,在本例中便是如 此。若是它找不到对应的方法,它就会在该类所属的基本类中依顺序逐个寻找属于基本类的 方法,这个基本类是在定义子类时后跟的元组指定的。

这里有一条有关术语的注释——若是继承元组中有超过一个类,这种情 况就会被称做多重继承

end 参数用在超类的 tell() 方法的 print 函数中,目的是打印一行并容许下一次打印在 同一行继续。这是一个让 print 可以不在打印的末尾打印出 n (新行换行符)符号的小 窍门。

面向对象经常使用术语

抽象/实现

抽象指对现实世界问题和实体的本质表现,行为和特征建模,创建一个相关的子集,能够用于 绘程序结构,从而实现这种模型。抽象不只包括这种模型的数据属性,还定义了这些数据的接口。

对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明并且无关的。

封装/接口

封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。经过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员容许这些操做。做为实现的 一部分,客户端根本就不须要知道在封装以后,数据属性是如何组织的。在Python中,全部的类属性都是公开的,但名字可能被“混淆”了,以阻止未经受权的访问,但仅此而已,再没有其余预防措施了。这就须要在设计时,对数据提供相应的接口,以避免客户程序经过不规范的操做来存取封装的数据属性。

注意:封装毫不是等于“把不想让别人看到、之后可能修改的东西用private隐藏起来”

真正的封装是,通过深刻的思考,作出良好的抽象,给出“完整且最小”的接口,并使得内部细节能够对外透明

(注意:对外透明的意思是,外部调用者能够顺利的获得本身想要的任何功能,彻底意识不到内部细节的存在)

合成

合成扩充了对类的 述,使得多个不一样的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,好比一个类由其它类组成,更小的组件也多是其它的类,数据属性及行为, 全部这些合在一块儿,彼此是“有一个”的关系。

派生/继承/继承结构

派生描述了子类衍生出新的特性,新类保留已存类类型中全部须要的数据和行为,但容许修改或者其它的自定义操做,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,能够述成一个“族谱”,连续的子类,与祖先类都有关系。

泛化/特化

基于继承
泛化表示全部子类与其父类及祖先类有同样的特色。
特化描述全部子类的自定义,也就是,什么属性让它与其祖先类不一样。

多态与多态性

多态指的是同一种事物的多种状态:水这种事物有多种不一样的状态:冰,水蒸气

多态性的概念指出了对象如何经过他们共同的属性和动做来操做及访问,而不需考虑他们具体的类。

冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,可是冰.变云(),与水蒸气.变云()是大相径庭的过程,虽然调用的方法都同样

自省/反射

自省也称做反射,这个性质展现了某对象是如何在运行期取得自身信息的。若是传一个对象给你,你能够查出它有什么能力,这是一项强大的特性。若是Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工做。还有那些特殊属性,像__dict__,__name____doc__

IT入门 感谢关注

练习地址:www.520mg.com/it

相关文章
相关标签/搜索