首页 >后端开发 >Python教程 >Python基础学习之类的介绍

Python基础学习之类的介绍

零下一度
零下一度原创
2017-07-19 23:47:441314浏览

  在Python中,首字母大写的名称指的是类。这个类定义中的括号是空的,因为我们要从空白创建这个类。我们编写了一个文档字符串,对这个类的功能作了描述。类中的函数称为方法。

  以Student类为例,在Python中,定义类是通过class关键字:

class Student(object):

    pass

  class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

9.1.1创建类

class Student(object):

 

    def __init__(self, name, score):

        self.name = name

        self.score = score

  1.方法__init__() 是一个特殊的方法,创建新实例时,Python都会自动运行它。开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。这个方法的定义中,形参self 必不可少,还必须位于其他形参的前面

  2.每个与类相关联的方法调用都自动传递实参self ,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。self 会自动传递,因此我们不需要传递它。

  3.以self 为前缀的变量都可供类中的所有方法使用,我们还可以通过类的任何实例来访问这些变量。

  4.self.name= name像这样可通过实例访问的变量称为属性

  5.面向对象编程的一个重要特点就是数据封装。可以直接在类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法.

9.1.2根据类创建实例

  我们通常可以认为首字母大写的名称(如Dog )指的是类,而 小写的名称(如my_dog )指的是根据类创建的实例。

  1、要访问实例的属性,可使用句点表示法,我们编写了如下代码来访问my_dog 的属性name 的值。

  my_dog.name

  句点表示法在Python中很常用,这种语法演示了Python如何获悉属性的值。

  2、根据Dog 类创建实例后,就可以使用句点表示法来调用Dog 类中定义的任何方法。

  3、可按需求根据类创建任意数量的实例。

9.2使用类和实例

  1.你需要执行的一个重要任务是修改实例的属性。你可以直接修改实例的属性,也可以编写方法以特定的方式进行修改。

  2.类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同。

9.2.1给类设定初始值

      类中的每个属性都必须有初始值,哪怕这个值是0或空字符串。在有些情况下,如设置默认值时,在方法__init__() 内指定这种初始值是可行的;如果你对某个属性这样做了,就无需包含为它提供初始值的形参。

直接在class中定义属性,这种属性是类属性:

class Student(object):

    name = 'Student'

  在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。

9.2.2修改属性的值

  可以以三种不同的方式修改属性的值:

  1.直接通过实例进行修改;

  2.通过方法进行设置;

  3.通过方法进行递增(增加特定的值)。

9.2.3访问限制

  1.在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。

  2.如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。

class Student(object):

    def __init__(self, name, score):

        self.__name = name

        self.__score = score

 

    def print_score(self):

        print('%s: %s' % (self.__name, self.__score))

  3.改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name和实例变量.__score了:

>>> bart = Student('Bart Simpson', 98)

>>> bart.__name

Traceback (most recent call last):

  File "", line 1, in

AttributeError: 'Student' object has no attribute '__name'

  4.这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。但是如果外部代码要获取name和score,可以给Student类增加get_name和get_score这样的方法:

class Student(object):

    ...

    def get_name(self):

        return self.__name

 

    def get_score(self):

        return self.__score

  5.如果又要允许外部代码修改score,可以再给Student类增加set_score方法:

class Student(object):

    ...

    def set_score(self, score):

        self.__score = score

  6.和原来直接调用参数相比,在方法中,可以对参数做检查,避免传入无效的参数:

class Student(object):

    ...

    def set_score(self, score):

        if 0 <= score <= 100:

self.__score = score

else:

raise ValueError('bad score')

  7.需要注意的是,在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

  8.双下划线开头的实例变量也不是一定不能从外部访问。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量。

9.3继承

  1.如果一个类似另一个类的特殊版本,可以使用继承,一个类继承另一个类它将自动获取另一个类的所有属性和方法。原有的类为父类,新类为子类。

  2.子类继承父类所有的属性和方法,还可以定义自己的属于和方法。在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

class Dog(Animal): #继承Animal

pass

9.3.1子类的方法__init__()

  1.继承需要给父类的所有属性赋值,子类的__init__()需要父类施以援手。

  2.并且父类必须在继承文件中,在子类之前。

  3.定义子类时,必须在括号中指定父类的名称。

  4.super()特殊函数,帮助python将父类和子类并联起来。父类也称超类,super的由来。

9.3.2定义子类的方法和属性

  让一个类继承一个类后。可添加区别子类和父类的属性和方法。

9.3.3重写父类

  对应父类的方法,只有不符合子类的需要都可以重写,在子类中添加新的方法描述子类的特点。去父类的去其糟怕取其精华。

9.3.4多态

  1.当子类和父类都存在相同的方法时,我们说,子类覆盖了父类的方法,在代码运行的时候,总是会调用子类的方法。这样,我们就获得了继承的另一个好处:多态

  2.所以,在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行。

  3.多态的好处是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思。

  4.对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

  • 对扩展开放:允许新增Animal子类;

  • 对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

9.3.5使用__slots__

  为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性。

class Student(object):

__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

  使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。

9.3.6多重继承

  1.通过多重继承,一个子类就可以同时获得多个父类的所有功能。

  2.设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich(鸵鸟)继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。

  3.这样我们就不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类。由于Python允许使用多重继承,因此,MixIn就是一种常见的设计。只允许单一继承的语言(如Java)不能使用MixIn的设计。

9.3.7定制类

  1.Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类。

__str__

  定义好__str__()方法,可以返回一个好看的字符串就:

>>> class Student(object):

...     def __init__(self, name):

...         self.name = name

...     def __str__(self):

...         return 'Student object (name: %s)' % self.name

...

>>> print(Student('Michael'))

Student object (name: Michael)

  这样打印出来的实例,不但好看,而且容易看出实例内部重要的数据。

  直接敲变量不用print,打印出来的实例还是不好看:

>>> s = Student('Michael')

>>> s

<__main__.Student object at 0x109afb310>

  这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

  解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法:

class Student(object):

    def __init__(self, name):

        self.name = name

    def __str__(self):

        return 'Student object (name=%s)' % self.name

    __repr__ = __str__

__iter__

  如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:

class Fib(object):

    def __init__(self):

        self.a, self.b = 0, 1 # 初始化两个计数器a,b

 

    def __iter__(self):

        return self # 实例本身就是迭代对象,故返回自己

 

    def __next__(self):

        self.a, self.b = self.b, self.a + self.b # 计算下一个值

        if self.a > 100000: # 退出循环的条件

            raise StopIteration()

        return self.a # 返回下一个值

  现在,试试把Fib实例作用于for循环:

>>> for n in Fib():

...     print(n)

...

1

1

2

3

5

...

46368

75025

__getitem__

  Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:

>>> Fib()[5]

Traceback (most recent call last):

  File "", line 1, in

TypeError: 'Fib' object does not support indexing

  要表现得像list那样按照下标取出元素,需要实现__getitem__()方法:

class Fib(object):

    def __getitem__(self, n):

        a, b = 1, 1

        for x in range(n):

            a, b = b, a + b

        return a

__getattr__

  Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值。已有的属性,比如name,不会在__getattr__中查找。

__call__

  1.任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。

  2.通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

9.3.8枚举类

  为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能:

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

9.3.9元类

type()

  要创建一个class对象,type()函数依次传入3个参数:

  1. class的名称;

  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;

  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

metaclass

  metaclass,直译为元类,简单的解释就是:当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

__new__()方法接收到的参数依次是:

  1. 当前准备创建的类的对象;

  2. 类的名字;

  3. 类继承的父类集合;

  4. 类的方法集合。

9.3.10将实例用作属性

  使用代码模拟实物时,当你添加越来越多的特点和细节是,会发现属性和方法以及代码文件都越来越长,这时候可以把其中一部分分离,重新组成一个类。可以将一个大类分成许多小类。

9.3.11模拟实物

  模拟较复杂的实物,需要从较高的逻辑层次考虑。为了编写效率更高,更简洁准确的代码,可能需要不停的重组类。

9.4导入类

  1.随着不断对类的添加,即使很优秀的继承也会是代码变得很长,所以python可以让你把类导入模块。并在主程序中导入使用所需要的模块。

  2.为自己的每个模块写文档字符串,说明模块的作用,对内容进行简要叙述。

  3.从一个模块导入多个类,用逗号隔开,并根据自己的需要创建实例。

  4.也可以导入整个类,并在实例前加上类名,和访问需要的类。

  5.在一个模块中导入另一个模块。

9.5python标准库(模块)

  Python标准库是一组模块,只需要import语句就可以导入使用。

  字典让你把信息关联起来,但不记录你添加键值对的顺序,要创建字典并记录添加键值对的顺序,可使用模块collections中的orderedDict类。OrderedDict的实例和字典一样,不过记录了添加键值对的添加顺序。

  OrderdDict很不错,兼具列表和字典的特点,有时候很需要。

random模块

  包含各种方式生成随机数的函数。其中randint()返回一个位于指定范围内的整数。

datetime是模块

  datetime模块还包含一个datetime类,通过from datetime import datetime导入的才是datetime这个类。如果仅导入import datetime,则必须引用全名datetime.datetime。

  datetime.now()返回当前日期和时间,其类型是datetime。

常用的第三方库

  还有MySQL的驱动:mysql-connector-python,用于科学计算的NumPy库:numpy,用于生成文本的模板工具Jinja2,等等。

  1.urlparse 模块,urlpasrse 模块提供了一些基本功能,用于处理 URL 字符串。这些功能包括 urlparse()、 urlunparse()和 urljoin()。

  2.urlparse()将 urlstr 解析成一个 6 元组(prot_sch, net_loc, path, params, query, frag)。前面已 经描述了这里的每个组件。

  3.urlunparse()的功能与 urlpase()完全相反,其将经 urlparse()处理的 URL 生成 urltup 这个 6 元组(prot_sch, net_loc, path, params, query, frag),拼接成 URL 并返回。

  4.urllib 模块提供了许多函数,可用于从指定 URL 下载数据,同时也可以对字符串进行编 码、解码工作,以便在 URL 中以正确的形式显示出来。下面将要介绍的函数包括 urlopen()、 urlretrieve()、quote()、unquote()、quote_plus()、unquote_plus()和 urlencode()。

  5.urlopen()打开一个给定 URL 字符串表示的 Web 连接,并返回文件类型的对象。

  6.urlretrieve()不是用来以文件的形式访问并打开 URL,而是用于下载完整的 HTML,把另存为文件。

 

9.6类编码风格

  熟悉与类相关的编码风格,当程序复杂时更应该这样。

  1.类名应采用驼峰命名法,即类的首字母大写,不含下划线,而实例名和模块名都首字母小写,并用下划线连接单词。

  2.在每个类定义后面添加文档字符串说明,简要描述类的功能作用,并且在每个模块下面都要添加文档字符串说明,对模块下的类可以做什么进行简要说明。

  3.用空行来组织代码,但不要滥用,在类中可用一个空行分隔方法,用两个空行分隔类。

  4.需要同时导入标准库中的模块和自己编写的模块时,先编写导入标准库模块的import语句,用一个空行隔开,在导入自己编写的模块import语句。在包含多个import语句时,可以让人容易弄明白程序中各个模块的由来。

以上是Python基础学习之类的介绍的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn