欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

Python基础学习之类的介绍

程序员文章站 2022-04-12 21:01:28
...
  在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 "<stdin>", line 1, in <module>

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 "<stdin>", line 1, in <module>

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基础学习之类的介绍的详细内容,更多请关注其它相关文章!