Python学习笔记(五)

Python学习笔记(五)

1.模板

使用模板的好处?

  • 提高代码的可维护性
  • 被其他地方引用,加快开发进度
  • 避免函数名和变量名冲突

模板的导入

一个.py文件就是一个模板,Python还引入了包的概念,防止命名冲突。
请注意,每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py可以是空文件,也可以有Python代码。
Python使用import语句导入一个模块。例如,导入系统自带的模块 math

import math  

我们可以认为math就是指向模板的变量,通过math可以访问模板中所有公开的变量、函数和类。
如果我们希望只导入模板中的某几个函数,而不是全部,则可以用一下的语句:

from math import sin cos log  

这样就导入了math模板里面的sincoslog三个函数,其他函数都没有导入进来。
注意:
import xx:导入模块对于模块中的函数,每次调用需要“模块.函数”来用。
from xx import fun: 直接导入模块中某函数,直接fun()就可用。

当使用 from...import 导入函数引起了冲突。这时,可以给函数起个“别名”来避免冲突:

from math import log
from logging import log as logger   # logging的log现在变成了logger  

安装第三方模块

在Python中,安装第三方模块,是通过包管理工具pip完成的。

pip install 模板名  

默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中,如果我们要添加自己的搜索目录,有两种方法:

  1. 直接修改sys.path,添加要搜索的目录:

    sys.path.append('/Users/python_code/my_py_scripts')
    

    这种方法是在运行时修改,运行结束后失效。

  2. 第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。

2.面向对象编程

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

类和实例

在Python中,定义类是通过class关键字:

class Student(object)  
    pass  

创建实例是通过类名+()实现的:

s = Student()  

可以自由的为实例对象添加属性,不过一般都是通过一个特殊方法__init__()方法强制指定对象属性,相当于java中类的构造函数:

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

s = Student('zhangsan','man')    
print(s.name)
print(s.gender) 
运行结果:
zhangsan
man  

__init__()方法的第一个参数永远是self,它代表类实例本身。在创建对象的时候self参数不需要传入。
为类定义访问数据的方法:

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

    def get_name(self):
        return self.name
    def get_gender(self):
        return self.gender
s = Student('zhangsan','man')
print(s.get_name())
print(s.get_gender())  
运行结果:  
zhangsan
man  

定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入。

访问限制

为了让类的属性不能被外部随意访问,可以将类属性设为私有。在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。

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

这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
同时我们需要通过方法来访问内部私有属性:

    def set_name(self,name):
        self.__name = name
    def set_gender(self,gender):
        self.__gender = gender    
    def get_name(self):
        return self.__name
    def get_gender(self):
        return self.__gender   

注意:在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。
私有变量也不是一定不能从外部直接访问,是Python解释器自动改了变量名:

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

    def set_name(self,name):
        self.__name = name
    def set_gender(self,gender):
        self.__gender = gender    
    def get_name(self):
        return self.__name
    def get_gender(self):
        return self.__gender
s = Student('zhangsan','man')
print(s._Student__name)
print(s._Student__gender)  
运行结果:  
zhangsan
man

不同的Python解析器可能会把__name改成不同的变量名,因此直接从外部修改私有变量的值不会成功,也不建议直接在外部访问私有变量。
以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

继承和多态

继承最大的好处是子类获得了父类的全部功能,子类只需要添加自己特有的功能。

class Animal(object):
    def run(self):
        print("Animal is runing")

class Dog(Animal):
    pass
class Cat(Animal):
    pass  
d = Dog()
c =Cat()

d.run()
c.run()  
运行结果:  
Animal is runing
Animal is runing  

在子类初始化的时候必须先通过super关键字调用父类的初始化方法:

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

s = Student('zhangsan',88)
print(s.name)
print(s.score)  
运行结果:  
zhangsan
88  

函数super(Student, self)将返回当前类继承的父类,然后调用__init__方法来初始化父类,否则继承自Person的Student类将没有name这一属性。

当子类中覆盖了父类中的方法,会调用子类自己的方法,而且子类实例既可以是子类类型,又可以是父类类型。

class Animal(object):
   def run(Animal):
        print("Animal is runing")        
class Dog(Animal):
    def run(self):
        print("Dog is runing")
class Cat(Animal):
    def run(self):
        print("Cat is runing")

a = Animal()
d = Dog()
c = Cat()
a.run()
d.run()
c.run()
print(isinstance(a,Animal))
print(isinstance(a,Dog))
print(isinstance(a,Cat))
print(isinstance(d,Animal))
print(isinstance(d,Dog))
print(isinstance(c,Animal))
print(isinstance(c,Cat))  

运行结果:  
Animal is runing
Dog is runing
Cat is runing
True
False
False
True
True
True
True  

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行。
多态的好处:在一个方法中,当我们传入子类型时,我们只需要接收父类型,然后会自动调用实际类型对应的方法。

class Animal(object):
    def run(self):
        print("Animal is runing")

class Dog(Animal):
    def run(self):
        print("Dog is runing")
class Cat(Animal):
    def run(self):
        print("Cat is runing")

def to_run(animal):
    animal.run()

a = Animal()
d = Dog()
c = Cat()

to_run(a)
to_run(d)
to_run(c)  
运行结果:  
Animal is runing
Dog is runing
Cat is runing

获取对象信息

type()

type()函数用来判断对象类型,基本类型都可以用type()判断:

print(type(123))
print(type('123'))
print(type([1,2,3]))
print(type((1,2,3)))  
运行结果:  
<class 'int'>
<class 'str'>
<class 'list'>
<class 'tuple'> 

如果一个变量指向函数或者类,也可以用type()判断:

print(type(abs))  
运行结果:
<class 'builtin_function_or_method'>

type()返回的是Class类型。如果要判断一个对象是否是函数,可以使用types模块中定义的常量:

import types
def fn():
    pass

print(type(fn) == types.FunctionType)
print(type(abs) == types.BuiltinFunctionType)
print(type(lambda x : x) == types.LambdaType)  
运行结果:  
True
True
True  
instance()

对于类的继承来说,使用instance()更方便

class A(object):
    pass
class B(A):
    pass
class C(A):
    pass

a = A()
b = B()
c = C()

print(isinstance(a,A))
print(isinstance(b,A))
print(isinstance(c,A))
运行结果:  
True
True
True  

还可以判断一个变量是否是某些类型中的一种:

print(isinstance(['1','2','3'],(list,tuple)))  
运行结果:  
True
dir()

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

print(dir('ABC'))  
运行结果:  
['__add__', '__class__', '__contains__', '__delattr__',   
'__dir__', '__doc__', '__eq__', '__format__',   
'__ge__', '__getattribute__', '__getitem__',   '__getnewargs__',  
 '__gt__', '__hash__', '__init__', '__iter__',   
'__le__', '__len__', '__lt__', '__mod__',  
 '__mul__', '__ne__', '__new__', '__reduce__',   
'__reduce_ex__', '__repr__', '__rmod__', '__rmul__',   
'__setattr__', '__sizeof__', '__str__', '__subclasshook__',  
 'capitalize', 'casefold', 'center', 'count',   
'encode', 'endswith', 'expandtabs', 'find',   
'format', 'format_map', 'index', 'isalnum',   
'isalpha', 'isdecimal', 'isdigit', 'isidentifier',   
'islower', 'isnumeric', 'isprintable', 'isspace',   
'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip',  
 'maketrans', 'partition', 'replace', 'rfind',   
'rindex', 'rjust', 'rpartition', 'rsplit',   
'rstrip', 'split', 'splitlines', 'startswith', 'strip',   
'swapcase', 'title', 'translate', 'upper', 'zfill']   

__xx__的属性和方法在Python中有特殊用途,比如len方法返回长度。当我们需要对象返回长度写,复写这个方法就可以了。
还可以通过getattr()setattr()以及hasattr()来直接操作一个对象的状态:

class Test(object):
    def __init__(self):
        self.x = 9
    def power(self):
        return self.x *self.x

t =Test()
print(hasattr(t,'x'))
print(hasattr(t,'power'))
setattr(t,"x",10)
print(getattr(t,"x"))
print(getattr(t,"power")())  
运行结果:  
True
True
10
100  

如果试图获取不存在的属性,会抛出AttributeErro的错误。可以传入一个default参数,如果属性不存在,就返回默认值:

print(getattr(t,'y',2))  
运行结果:  
2

注意:只有在不知道对象信息的时候,我们才会去获取对象信息

实例属性和类属性

直接在class中定义的属性,这种属性是类属性,归类本身所有。而实例属性是通过实例绑定或者self绑定的。实例属性每个实例各自拥有,互相独立,而类属性有且只有一份
类属性直接与类绑定,因此访问的类属性可以不需要创建类的实例,当然也可以通过实例访问:

class Person(object):
    name = 'zhangsan' 
p = Person()
print(p.name)
print(Person.name)
运行结果:  
zhangsan
zhangsan  

当修改类属性时,所有实例都会受到影响,在实例变量上修改类属性,只是相当于为该实例添加了一个实例属性而已:

class Person(object):
    name = 'zhangsan' 
p1 = Person()
p2 = Person()
p2.name = 'lisi'
print(p1.name)
print(p2.name)
print(Person.name)  
运行结果:  
zhangsan
lisi
zhangsan

可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。

面向对象的高级编程

使用slots

顾名思义,__slots__是指一个类允许的属性列表,来限制该class实例能添加的属性。 为对象绑定不在属性列表里面的属性,程序将会得到AttributeError 错误:

class Person(object):
    __slots__ = ('name','gender')

p = Person()
p.score = 6  
运行结果:  
Traceback (most recent call last):
  File "D:\python_code\pythonTest\hello.py", line 5, in <module>
    p.score = 60
AttributeError: 'Person' object has no attribute 'score' 

注意:__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

class Person(object):
    __slots__ = ('name', 'gender')
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Student(Person):
    __slots__ = ('score')
    def __init__(self,name,gender,score):
        super(Student,self).__init__(name,gender)
        self.score = score

s = Student('Bob', 'male', 59)
s.name = 'Tim'
s.score = 99
print (s.score)  
运行结果:  
99

使用@property

@property是一个装饰器。目的是把属性的get/set 方法“装饰”成属性调用:

class Student(Person):
    def __init__(self,name,gender,score):
        super(Student,self).__init__(name,gender)
        self._score = score        
    @property   
    def score(self):
        print("score(self) be called")
        return self._score
    @score.setter
    def score(self,value):
        print("score(self,value) be called")
        if not isinstance(value,int):
            raise ValueError('score must be a integer')
        if value < 0 or value > 100:
            raise ValueError('value must between 0~100')
        self._score = value  

s = Student('Bob', 'male', 59)
s.score = 5

print (s.score)  
运行结果:  
score(self,value) be called
score(self) be called
5  

从上面的打印结果可以看出,s.score = 5相当于在在执行set方法,s.score相当于在执行get方法。

多重继承

多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。

class A(object):
    def __init__(self, a):
        print ('init A...')
        self.a = a

class B(A):
    def __init__(self, a):
        super(B, self).__init__(a)
        print ('init B...')

class C(A):
    def __init__(self, a):
        super(C, self).__init__(a)
        print ('init C...')

class D(B, C):
    def __init__(self, a):
        super(D, self).__init__(a)
        print ('init D...')

定制类

通过复写某些特殊意义的方法来自定义类。

__str__()

__str__()方法类似java中的toString()方法:

class Person(object):
    def __init__(self,name,gender):
        self.name = name
        self.gender = gender
    def __str__(self):
        return 'Person(%s,%s)' %(self.name,self.gender)
p =Person('lisi','man')
print(p)  
运行结果:  
Person(lisi,man)  

因为 Python 定义了__str__()__repr__()两种方法,__str__()用于显示给用户,而__repr__()用于显示给开发人员。一种偷懒的方式:

 __repr__ = __str__  
__cmp__()

Python的 sorted() 按照默认的比较函数cmp排序,但是,如果对一组 Student 类的实例排序时,就必须提供我们自己的特殊方法 __cmp__()

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def __str__(self):
        return '(%s: %s)' % (self.name, self.score)
    __repr__ = __str__

    def __cmp__(self, s):
        if self.name < s.name:
            return -1
        elif self.name > s.name:
            return 1
        else:
            return 0
__len__()

要让 len() 函数工作正常,类必须提供一个特殊方法__len__(),它返回元素的个数。

class Student(object):
    def __init__(self,*args):
        self.names = args

    def __len__(self):
        return len(self.names)
s = Student('Mike','Jane','molly')
print(len(s))  
运行结果:  
3  
__iter__()

如果一个类想被用于for...in,就必须实现一个__iter__()

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 # 返回下一个值  
__getitem__()

按照下标取出元素,需要实现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__()

只有在没有找到属性的情况下,才调用__getattr__

class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99  
__call__()

一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法__call__()

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

    def __call__(self, friend):
        print 'My name is %s' % self.name
        print 'My friend is %s' % friend  
p = Person('Bob','man')  
p('Tim')
运行结果:  
My name is Bob  
My friend is Tim  

使用枚举类

Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较

from enum import Enum

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

for name,member in Month.__members__.items():
    print('name = %s,member = %s value = %s' % (name,member,member.value))  
运行结果:  
name = Jan,member = Month.Jan value = 1
name = Feb,member = Month.Feb value = 2
name = Mar,member = Month.Mar value = 3
name = Apr,member = Month.Apr value = 4
name = May,member = Month.May value = 5
name = Jun,member = Month.Jun value = 6
name = Jul,member = Month.Jul value = 7
name = Agu,member = Month.Agu value = 8
name = Sep,member = Month.Sep value = 9
name = Oct,member = Month.Oct value = 10
name = Nov,member = Month.Nov value = 11
name = Dec,member = Month.Dec value = 12  

value属性则是自动赋给成员的int常量,默认从1开始计数。
如果需要更精确地控制枚举类型,可以从Enum派生出自定义类,@unique装饰器可以帮助我们检查保证没有重复值:

from enum import Enum,unique
@unique
class Weekday(Enum):
    Sun = 0 
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

day1 = Weekday.Sun
print(day1)
print(Weekday.Mon)
print(Weekday['Tue'])
print(Weekday.Wed.value)
print(Weekday(4))  
运行结果:  
Weekday.Sun
Weekday.Mon
Weekday.Tue
3
Weekday.Thu  

参考资料:廖雪峰的官方网址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值