python语言基础笔记_Python3.6学习笔记总结(三),看完这些,Python你就基本掌握了

原标题:Python3.6学习笔记总结(三),看完这些,Python你就基本掌握了

学习Python语言的,这本书可以参考参考哦,自己还是要拥有一本属于自己的书的,因为你在忘记的时候可以经常拿出来翻阅一下,巩固一下。

文章内容

面向对象编程

面向对象编程 Object Oriented Programming 简称 OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。

而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。

classStudent(object):

def__init__(self, name, score):

self.name = name

self.score = score

defprint_score(self):

print( '%s: %s'% (self.name, self.score)) 类和实例

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

定义好了Student类,就可以根据Student类创建出Student的实例,创建实例是通过类名+()实现的。__init__函数与其它函数有所不同,它的第一个参数永远是实例变量self,并且,调用时,不用传递该参数。

数据封装

类本身拥有数据和方法,相当于将“数据”封装起来了。对于外部来说,并不需要知道内部的逻辑。

访问限制

Class可以有属性和方法,我们可以对属性和方法进行控制,以达到允许或者不允许外部访问的目的。如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。

classStudent(object):

def__init__(self, name, score):

self.__name = name

self.__score = score

defprint_score(self):

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

defget_name(self):

returnself.__name

defget_score(self):

returnself.__score

defset_score(self, score):

if0<= score <= 100:

self.__score = score

else:

raiseValueError( 'bad score’)

在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。继承和多态

在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

classAnimal(object):

defrun(self):

print( 'Animal is running…')

classDog(Animal):

defrun(self):

print( 'Dog is running...')

classCat(Animal):

defrun(self):

print( 'Cat is running…')

当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行。

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

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

对修改封闭:不需要修改依赖Animal类型的外部函数。

鸭子类型

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

获取对象信息

判断Python中对象的类型,可以用以下方法。

type()

基本类型都可以用type()判断,基本数据类型可以直接写int、str,判断是否函数需要使用types中定义的常量。

>>>importtypes

>>>deffn():

...pass

...

>>>type(fn)==types.FunctionType

True

>>>type(abs)==types.BuiltinFunctionType

True

>>>type( lambdax: x)==types.LambdaType

True

>>>type((x forx inrange( 10)))==types.GeneratorType

Trueisinstance()

对于类和实例,使用type()就不是很方便,可以使用isinstance()。基本数据类型也可以使用isinstance()判断。还可以判断一个变量是否是某些类型中的一种。

>>>isinstance([ 1, 2, 3], (list, tuple))

True

>>>isinstance(( 1, 2, 3), (list, tuple))

Truedir()

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法。

实例属性和类属性

Python类创建的实例可以任意绑定属性,如果需要对类本身绑定属性,则需要在类中定义,这就区分了类属性和实例属性。

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

数据封装、继承和多态是面相对象程序设计中的三个基本概念,另外还有很多特性,包括多重继承、定制类等。

使用 slots()

在Python中,可以对类动态的增加属性和方法,这在静态语言中很难实现。

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

classStudent(object):

def__init__(self):

print( "Instance Created")

Tracy = Student()

Tracy.age = 30

Bob = Student()

Bob.age = 41

Ceaser = Student()

print(Tracy.age)

print(Bob.age)

defset_age(self, age):

self.age = age

fromtypes importMethodType

s = Student()

s.set_age = MethodType(set_age, s)

s.set_age( 25)

print(s.age)

Student.set_age = set_age

Ceaser.set_age( 33)

print(Ceaser.age)

但这也带来一个问题,属性和方法可以随意更改,如果我们要限制怎么办?可以使用__slots。Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性。使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。

classStudent(object):

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

Python中实例的属性暴露在外面可以随便修改,这样就无法保证属性的有效性符合校验规则。虽然可以通过设置Setter和Getter来进行检查,但如果属性特别多,操作起来又比较麻烦。

还记得装饰器(decorator)可以给函数动态加上功能吗?对于类的方法,装饰器一样起作用。Python内置的@property装饰器就是负责把一个方法变成属性调用的。

classStudent(object):

@property

defscore(self):

returnself._score

@score.setter

defscore(self, value):

ifnotisinstance(value, int):

raiseValueError( 'score must be an integer!')

ifvalue < 0orvalue > 100:

raiseValueError( 'score must between 0 ~ 100!')

self._score = value

@property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作。

>>>s = Student()

>>>s.score = 60# OK,实际转化为s.set_score(60)

>>>s.score # OK,实际转化为s.get_score()

60

>>>s.score = 9999

Traceback (most recent call last):

...

ValueError: score must between 0~ 100!

还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性。

classStudent(object):

@property

defbirth(self):

returnself._birth

@birth.setter

defbirth(self, value):

self._birth = value

@property

defage(self):

return2015- self._birth 多重继承

继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能。举例来说对于动物的对象设计,可以按照“哺乳动物”、“鸟类”来设计分类对象,按照不同的维度,也可以按照“能跑的”、“能飞的”或者“宠物”、“非宠物”设计分类,如果按照单一继承的方式,类的设计就像下图,会变的非常复杂。

5581f3924a5a411ebb5583fefd657a92.png

正确的办法是采用多重继承。一个子类就可以同时获得多个父类的所有功能。在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。

classAnimal(object):

pass

# 大类:

classMammal(Animal):

pass

classBird(Animal):

pass

# 各种动物:

classDog(Mammal):

pass

classBat(Mammal):

pass

classParrot(Bird):

pass

classOstrich(Bird):

pass

classRunnable(object):

defrun(self):

print( 'Running...')

classFlyable(object):

deffly(self):

print( 'Flying…’)

class Dog(Mammal, Runnable):

pass

class Bat(Mammal, Flyable):

pass

Python自带的很多库也使用了MixIn。举个例子,Python自带了TCPServer和UDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixIn和ThreadingMixIn提供。通过组合,我们就可以创造出合适的服务来。

定制类

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

str

定义print 函数调用时的返回结果。

>>>classStudent(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) repr

定义返回程序开发者看到的字符串,也就是在命令行状态下执行时的返回值。

classStudent(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错误时退出循环。

classFib(object):

def__init__(self):

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

def__iter__(self):

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

def__next__(self):

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

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

raiseStopIteration()

returnself.a # 返回下一个值getitem

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

classFib(object):

def__getitem__(self, n):

ifisinstance(n, int): # n是索引

a, b = 1, 1

forx inrange(n):

a, b = b, a + b

returna

ifisinstance(n, slice): # n是切片

start = n.start

stop = n.stop

ifstart isNone:

start = 0

a, b = 1, 1

L = []

forx inrange(stop):

ifx >= start:

L.append(a)

a, b = b, a + b

returnL

通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

getattr

正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。要避免这个错误,除了可以加上一个属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。

classStudent(object):

def__init__(self):

self.name = 'Michael'

def__getattr__(self, attr):

ifattr== 'score':

return99

call

任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。__call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

classStudent(object):

def__init__(self, name):

self.name = name

def__call__(self):

print( 'My name is %s.'% self.name) 使用枚举类

之前说到过,Python中其实不存在常量,但是可以通过枚举类的方式来变通实现。

fromenum importEnum

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

forname, member inMonth.__members__.items():

print(name, '=>', member, ',', member.value)

fromenum importEnum, unique

@unique #@unique装饰器可以帮助我们检查保证没有重复值。

classWeekday(Enum):

Sun = 0# Sun的value被设定为0

Mon = 1

Tue = 2

Wed = 3

Thu = 4

Fri = 5

Sat = 6

这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员。

使用元类 type()

type()函数可以查看一个类型或变量的类型,Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello。我们说class的定义是运行时动态创建的,而创建class的方法就是使用type()函数。

type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)…的定义。

>>>deffn(self, name='world'):# 先定义函数

...print( 'Hello, %s.'% name)

...

>>>Hello = type( 'Hello', (object,), dict(hello=fn)) # 创建Hello class

>>>h = Hello()

>>>h.hello()

Hello, world.

>>>print(type(Hello))

< class'type'>

>>> print(type(h))

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

1.class的名称;

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

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

通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

metaclass

metaclass,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。

# metaclass是类的模板,所以必须从`type`类型派生:

classListMetaclass(type):

def__new__(cls, name, bases, attrs):

attrs[ 'add'] = lambdaself, value: self.append(value)

returntype.__new__(cls, name, bases, attires) 返回搜狐,查看更多

责任编辑:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值