Python函数(第三章:类和对象)

python如何将函数和数据整合在一起,并且通过一个对象的名称访问它们。

如何和为什么使用类与对象,以及他们如何使编程人员易于多种情形下编写和使用程序。

3.1考虑编程

现在要在python中创建一个对对象的描述,您已有足够的只是获得两个视图。第一个是数据视图,除了顶层或者全局作用域的数据外,可以根据需要使用和清除它们。另一个函数视图,他们没有固有的数据,而是操作提供给他们的数据。

3.1.1对象的含义

任何一个数据都有对象,每个对象都由3部分组成:标识,类型和值。对象的标识代表该对象在内存中存储的位置(不可改变的),对象的类型表明它可以拥有数据和值的类型,在对象中,可变类型的值可以更改,不可变类型的值不能更改。

简单些的解释是参考本书中已经介绍的对象。例如,整型,字符串,列表等都是对象。可以在程序中很方便地使用这些对象,但是将关系紧密的对象整合在一起岂不是更有意义?这就是类的由来,类允许定义一组对象,并将他们封装到一个方便的空间去。

3.1.2已经了解的对象

3.1.3如何使用对象

 

3.2定义类

当思考包含几百行python代码的小程序如何运行时,经常可以发现程序将数据组织成组的形式——当访问某个数据时,将影响与该数据一起协作的数据。经常会碰到有依赖关系的完整数据列表,如列表1的第一个元素和列表2和列表3中的第一个元素匹配。有时,必须通过创造性地将这些列表组合起来才能解决这个问题。python运用了创建用作占位符的整个类的概念,类起了占位符的作用,当一个类被调用时,它创建了绑定到一个名称的对象。

3.2.1如何创建对象

定义一个类

使用关键字class,在后面紧跟一个名称来完成。

class Fridge:

 

由类创建对象

>>>f=Fridge()

此时还没有定义任何复杂的类,Fridge类基本是空的,它作为一个起点。然而,即使他是空的,也应当注意到已经创建了一个可用的空类,它几乎不进行任何操作。

花上几分钟看_init_和self部分,则是类的两个非常重要的特征,当python创建对象时,_init_方法传递给对象第一个参数。而self实际上是代表该实例本身的变量。

编写内部方法

这个内部方法,不能判断当前传入的类型是否有效,应当用接口函数去检测。在允许的每个地方做检查是一个好想法,但在当前这个例子中,不打算在此处检查,因为只会以非常简单的方式使用_add_multi方法。

编写接口方法

为了更快捷,现在可以不输入文档字符串,此处的方法使您在遇到问题时,可以更好地理解代码的实际操作。

这些方法需要缩进在Fridge类的定义中,看上去每行的初始位置开始的任何代码实际上是前一行的延续,应当输入到同一行上:

        def  add_one(self, food_name):
        if type(food_name) != type(""):
            raise TypeError,"add_one requires a string,given a %s"% type
                (food_name)
        else:
            self._add_multi(food_name,1)

        return True


        def add_many(self, food_dict):
            if type(food_dict) != type({}):
                raise TypeError("add_many requires a dictionary,got a %s" food_dict)

        for item in food_dict.keys():
            self._add_multi(item, food_dict[item])
        return

add_one和add_many的目的是类似,并且每个方法都有可以确保他们被正确使用的代码,他们都可以使用add-multi来完成主要工作。现在,如果add-multi的工作方式分发生改变,开发人员可以节省时间,因为它将自动改变使用它的两个方法的行为方式。

 现在已经编写了足够的代码,可以把食物放入Frige对象中,但是没有方法可以将放入冰箱的食物拿出来。可以直接访问object.items字典,但是除了测试的时候,这种不是一个好主意。但是现在就是测试,为何不这样做呢?

>>>f = Fridge({"eggs":6,"milk":4,"cheese":3})
>>>f.items
{cheese:3,eggs:6,milk:4}
>>>f.add_one("grape")
True
>>>f.items
{cheese:3,eggs:6,grape:1,milk:4}
>>>f.add_many({"mushroom":5,"tomato":3})
>>>f.items
{"tomato":3,cheese:3,grape:1,mushroom:5,eggs:6,milk:4}

目前为止输入的代码都能正常工作了,接下来需要增加可以判断冰箱中是否存在某物的方法。

编写代码真是某物是否在冰箱中存在很重要,因为它可以用于取出食物的方法中,如get_one,get_many,get_ingredients,从而使这些方法可以检查冰箱中是否有足够多所需的食物,这正是has和has_various方法的用途。

def has(self,food_name,quantity=1):
    return self.has_various({food_name:quantity})

def has_various({self,foods):
    try:
        for food in foods.keys():
            if self.items[food] < foods[food]:
                return False
            return True
    except keyError:
            return False

 

使用更多的方法:

现在可以使用python-i或者Run with Interpreter命令调用ch6.py文件,这样可以使用调价到Fridge类的任何代码。如果出现错误而不是>>>提示符,注意抛出的异常,并试图修复缩进问题,拼写错误或者其他基本错误。

Fridge类可按下述方法使用:

>>>f = Fridge({"eggs":6,"mike":4,"cheese":3})
>>>if f.has("cheese",2):
...        print("its time to make an omelet")
...
its time to make an omelet

实例说明:现在已经定义了新的方法,f对象可以使用它们,当用鸡蛋牛奶以及奶酪重新创建f对象时,就从新的fridge类创建对象,因此它拥有新添加的可用方法。

最后,我们应当讨论从冰箱中取食物的方法了。与向冰箱中添加食物的方法类似,由一个核心方法完成主要工作,所有接口方法都是依赖于这个方法。

def _get_multi(self,food_name,quantity):
     try:
            if(self.items[food_name] is None):
                return False;
            if(quantity>self.items[food_name]):
                return False;
            self.items[food_name] = self.items[food_name]-quantity
    except KeyError:
            return False
    return quantity

定义后,可以创建文档字符串指定的方法,其中每个方法都是使用_get_multi,因此都可以最少的额外代码从冰箱中取出食物:

3.2.2对象和它们的作用域

函数为他们使用的名称创建了自己的空间,也就是作用域。当函数被调用时,声明了名称并赋予值之后,只要函数还在使用,任何对该名称做出的修改会持续下去。然而,在函数结束运行后,并在此被调用,之前电泳过程中所做的工作都丢失了,改函数必须重新开始执行。

对象中的值可被存储的,并关联在self中,就是说self是指向对象的。

创建另外一个类:

我们创建了一个fridge类,现在创建omelet类:

现在有一个类,它目的很明确。omelet类有接口方法,使得它可与fridge对象协作,它仍具备创建指定煎蛋卷的功能。

下面代码都必须在omelet类定义下缩进一个级别:

def _ingredients_(self):
    return self.needed_ingredients

def get_kind(self):
    return self.kind

def set_kind(self,kind):
    possible_ingredients = self._known_kinds(kind)
    if possible_ingredients == False:
        return False
    else:
        self.kind = kind
        self.needed_ingredients = possible_ingredients

def set_new_kind(self,name,ingredients):
      self.kind = name
      self.needed_ingredients = ingredients
       return

def _known_kind(self,kind):
    if kind == "cheese":
        return {"eggs":2,"mike":1,"cheese":1}
    elif kind == "mushroom":
        return {"eggs":2,"mike":1,"cheese":1,"mushroom":2}
     elif kind == "onion":
        return {"eggs":2,"mike":1,"cheese":1,"onion":1}
    else:
        self.kind = kind
        self.needed_ingredients = ingredients
        return

def get_kown_kinds(self,fridge):
     self.from_fridge = fridge.get_ingredients(self)

def mix(self):
     for ingredient in self.from_fridge.keys():
          print("Mixing %d %s for the %s omelet"% self.from_fridge[ingredient],ingredient,self.kind)
     self.mixed = true

def make(self):
     if self.mixed ==true:
        print("cooking the %s omelet!"% self.kind)
        self.cooked = true        

现在有一个omelet类可以创建omelet对象,omelet类与煎蛋卷的过程有相同的特性。并且Omelet的外在表现集中到几个简单的接口

现在有两个类,用python -i或者run with Interpreter命令加载他们后,可制作一个煎蛋卷:

我们还可以制作多个煎蛋卷:

这样的编程方式,我们称之为面向对象,为什么会用于编制大型系统。

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。