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个参数:
class的名称;
继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
metaclass
metaclass,直译为元类,简单的解释就是:当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
__new__()方法接收到的参数依次是:
当前准备创建的类的对象;
类的名字;
类继承的父类集合;
类的方法集合。
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基础学习之类的介绍的详细内容,更多请关注其它相关文章!
上一篇: python怎么看数据类型