您的位置:首页 > 编程语言 > Python开发

python学习——基础(七)

2015-10-23 00:00 671 查看
摘要: 模块、面向对象编程、面向对象特性编程

在Python中,一个.py文件就称之为一个模块(Module)。

使用模块有什么好处?
最大的好处是大大提高了代码的可维护性。其次,编写代码不必从零开始。当一个模块编写完毕,就可以被其他地方引用。我们在编写程序的时候,也经常引用其他模块,包括Python内置的模块和来自第三方的模块。
模块别名:

try:
import cStringIO as StringIO
except ImportError: # 导入失败会捕获到ImportError
import StringIO

面向对象的访问限制:

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

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

class Student(object):
def __init__(self, name):
self.__name = name

def getName(self):
return '实例变量:'+self.__name

stu1 = Student('syc')
print stu1.getName() # 实例变量:syc
print stu1.__name # AttributeError: type object 'Student' has no attribute '__name'


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

继承:

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

class Animal(object):
def run(self):
print 'animail is running'

def eat(self):
print 'animail is eating'

class Horse(Animal):

def run(self):
print 'horse is running'
Animal.eat(self) # 子类中调用父类的方法

hs = Horse()
hs.run()
print type(hs)

结果:
horse is running
animail is eating
<class '__main__.Horse'>


如果要获得一个对象的所有属性和方法,可以使用dir()函数:

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'eat', 'run']


仅仅把属性和方法列出来是不够的,配合getattr()
、setattr()
以及hasattr()
,我们可以直接操作一个对象的状态:

class Horse(object):
name = 'horse'
def run(self):
print 'horse is running'
Animal.eat(self) # 子类中调用父类的方法

hs = Horse()
print hasattr(hs, 'name') # 判断hs对象是否含有name属性,同样也可以进行方法的判断
setattr(hs, 'age', '18') # 增加的是实例变量,不能为对象设置新的方法
print getattr(hs, 'age') # 得到hs的age值,同时可以得到方法的变量


使用__slots__

正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性

class Student(object):
name = 'syc'

def drink(self):
print 'student is drunking'

stu = Student()
stu.age = 18 #给实例增加属性
print stu.age
Student.age =19 # 给类添加属性
print Student.age

def eat(self):
print 'student is eating'

from types import MethodType
stu.eat = MethodType(eat, stu, Student) # 给Student的实例stu添加方法
stu.eat()

stu2 = Student()
# print stu2.eat() # AttributeError: 'Student' object has no attribute 'eat'

Student.eat = MethodType(eat, None, Student) # 给类Student添加方法
stu2.eat()


但是,如果我们想要限制class的属性怎么办?
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class能添加的属性和方法
class Student(object):
__slots__ = ('name', 'age', 'eat')
name = 'syc'

def drink(self):
print 'student is drunking'
stu = Student()
stu.address = 'fuzhou'

结果:AttributeError: 'Student' object has no attribute 'address'

方法也是同样的;

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

class Student(object):
__slots__ = ('name', 'age', 'eat')
name = 'syc'

def drink(self):
print 'student is drunking'

class SubStudent(Student):
pass

substu = SubStudent()
substu.address = 'fuzhou'
print substu.address  # fuzhou


使用@property

@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

class Student(object):

@property
def aname(self):  # 第一处
return self.__name

@aname.setter # 第二处
def name(self, name): # 第三处
# if name == 'syc':
#     raise TypeError('name不能为syc')
self.__name = name

stu = Student()
stu.name = 'syc' # 第四处
print stu.name # 第五处


第一处和第二处的name必须相同,第三处、第四处、第五处必须相同
新增的属性为name,不是__name

多重继承:

Mixin:在设计类的继承关系时,通常,主线都是单一继承下来的,如果需要“混入”额外的功能,通过多重继承就可以实现,这种设计通常称之为Mixin;python中的多重继承类似于java中的类组合;

在多重继承中,最大的问题就是当继承的父类中有同名的方法,这时的优先级问题:

class Grandfa(object):
def hair(self):
print 'no hair'

class Father(Grandfa):
pass

class Mom(object):
def hair(self):
print 'hair'

class Tom(Father,Mom):
pass

tom = Tom()
tom.hair()  # no hair 深度优先


从例子可以看出,遵从的是深度优先的准则;

定制类:

通过特殊变量,可以为我们定制类,如:__slots__,__len__,__str__等

__str__:

class Student(object):
pass
# def __str__(self):
#     return 'a student instance'

stu = Student()
print stu # <__main__.Student object at 0x02634B90>

class Student(object):
# pass
def __str__(self):
return 'a student instance'

stu = Student()
print stu # a student instance


还有一个类似的__repr__;
两者的区别是__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,也就是说,__repr__()
是为调试服务的。

__iter__:

判断对象是否可以进行迭代:

class Student(object):
pass

from collections import Iterable
print isinstance(stu,Iterable) # False


如果需要使对象能够迭代,可以在类中定义__iter__方法:

class Student(object):

def __iter__(self):
return self

def next(self):
self.a, self.b = self.b, self.a + self.b
if self.b >100:
raise StopIteration()
return self.b

stu = Student()

from collections import Iterable
print isinstance(stu,Iterable) # True
stu.a, stu.b = 1,2
print [x for x in stu] # [3, 5, 8, 13, 21, 34, 55, 89]


Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

__getitem__

虽然能作用于for循环

class Student(object):

def __getitem__(self, n):
for x in range(n):
self.a, self.b = self.b, self.a + self.b
return self.b

stu = Student()

from collections import Iterable
print isinstance(stu,Iterable) # False
stu.a, stu.b = 1,2
print stu[1] # [3, 5, 8, 13, 21, 34, 55, 89]


,但是不能像list按照下标取出元素,需要实现__getitem__方法:

__getattr__

通过__getattr__动态的定义类的属性和方法;

class Student(object):
def __init__(self, name):
self.name = name

def __getattr__(self, item):
if item == 'score': # 自动添加属性
return 99

if item =='getSocre': # 自动添加方法
return lambda :self.score * self.score

stu = Student('syc')
print stu.socre # None ==>当类定义了__getattr__时,即使没有定义socre,也不会报错:AttributeError: 'Student' object has no attribute 'socre'
print stu.score # 99
print stu.getSocre() # 9801


class Student(object):

def __init__(self, path=''):
self.__path = path
def __getattr__(self, item):
if item == "users":
return lambda user: Student("%s/users/:%s" % (self.__path, user))
else:
return Student("%s/%s" % (self.__path, item))

def __str__(self):
return self.__path

print Student().users('syc').repos # /users/:syc/repos


或者:

class Student(object):

def __init__(self, path=''):
self.__path = path
def __getattr__(self, item):
return Student("%s/%s" % (self.__path, item))

def __str__(self):
return self.__path

def __call__(self, name):
return Student("%s/:%s" % (self.__path, name))

print Student().users('syc').repos # /users/:syc/repos


__call__

一个对象实例可以有自己的属性和方法,

当我们调用实例方法时,我们用instance.method()来调用。能不能直接在实例本身上调用呢?类似instance()?在Python中,答案是肯定的。
任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。请看示例:
class Student(object):
def __init__(self, name):
self.name = name

def __call__(self):
print('My name is %s.' % self.name)

s = Student('syc')
s() # My name is syc.


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

那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call()__的类实例:

class Student(object):
def __init__(self, name):
self.name = name

def __call__(self):
print('My name is %s.' % self.name)

s = Student('syc')
print callable(s) # True
print callable([1, 2, 3]) # False
print callable('ABC') # False
print callable(None) # False


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

使用元类:

创建类有两种方法:自定义:class XXX,type(ClassName, parentClass, dict{fun,property})

def fn(self, name):
print 'Hello %s'% name
Hello = type('Hello', (object,), dict(hello=fn))
Hello().hello('world') # Hello world


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

class的名称;

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

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

通过type()
函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()
函数创建出class。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: