Python编程进阶——面向对象编程与面向对象高级编程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/iroy33/article/details/102543308

Preface:还是一步步脚踏实地,先把Python进阶学了orz,主要参考廖雪峰

使用模块

作用域

正常的函数和变量名是公开的(public),可以被直接引用,比如:abcx123PI

__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author____name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,不能用__name____score__这样的变量名。

类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc等(private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量)“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。

模块搜索路径

当我们试图加载一个模块时,Python会在指定的路径下搜索对应的.py文件,如果找不到,就会报错:

>>> import mymodule
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named mymodule

默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:

>>> import sys
>>> sys.path
['', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', ..., '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']

如果我们要添加自己的搜索目录,有两种方法:

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

>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')

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

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

类和实例

http://yangcongchufang.com/%E9%AB%98%E7%BA%A7python%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/python-object-class.html

获取对象信息(不知道对象信息的时候使用)

type()  对象可以是基本类型、函数、类,返回的是对应的Class类型

>>> type(123)==int
True

>>> import types
>>> type(lambda x:x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True


isinstance()

>>> isinstance(56,int)
True
>>> isinstance([1,2,3],(list,tuple)) 是否属于其中一种
True

dir() 获得一个对象的所有属性和方法,返回一个包含字符串的list

>>> dir('abc')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getatt
ribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__',
'__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '_
_rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encod
e', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isd
igit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower',
'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'sp
litlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
 

不带__的是普通属性或方法,a.strip()

带__xxx__在Python中有特殊用途 len('ABC)=='ABC'.__len__()

hasattr(obj,attr)  True or False

setattr(obj,attr,value)

getattr(obj,attr) 如果试图获取不存在的属性,会抛出AttributeError的错误,可以传入一个default参数,如果属性不存在,就返回默认值:getattr(obj,attr,404)

tips:可以用变量指向getattr获取的属性

>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn() # 调用fn()与调用obj.power()是一样的

类的属性

类本身的属性

实例属性属于各个实例所有,互不干扰;

类属性属于类所有,所有实例共享一个属性;

不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。

class Student(object):
    count=0

    def __init__(self,name):
        self.name=name
        Student.count+=1

if Student.count!=0:
    print('F')
else:
    bart=Student('Bart')
    if Student.count!=1:
        print('F')
    else:
        lisa=Student('Lisa')
        if Student.count==2:
            print('T')

面向对象高级编程

使用__slots__ 限制属性

__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

class Students(object):
    __slots__ = ('name')

bart=Students()
bart.name='bart'
bart.sex='m'

AttributeError: 'Students' object has no attribute 'sex' 如果没有slots的话,是可以给实例添加属性的

使用__property__ 将方法当做属性用

装饰器可以给函数动态加上功能

class Students(object):
    @property
    def score(self):
        return self._score

    @score.setter
    def score(self,value):
        if not isinstance(value,int):
            raise ValueError('must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('range wr')
        self._score=value


bart=Students()
# bart.score=9999
bart.score=100

MixIn 多重继承

定制类

__str__:定义如果打印实例返回什么信息

class Student(object):
    count=0

    def __init__(self,name):
        self.name=name
        Student.count+=1
    def __str__(self):
        return 'Screen object (name:%s)' % self.name
    __repr__=__str__
print(Student('yry'))

直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。解决办法是再定义一个__repr__()。但是通常__str__()__repr__()代码都是一样的,所以,有个偷懒的写法:__repr__=__str__

__iter__: 对类进行循环

class Fib(object):
    def __init__(self):
        self.a,self.b=0,1

    def __iter__(self):
        return self             #实例本身就是迭代对象,因此返回自己

    def __next__(self):
        self.a,self.b=self.b,self.a+self.b #由此可见,加的是原来的a值
        if self.a>10000:
            raise StopIteration
        return self.a

for n in Fib():
    print(n)

__getitem__:取类中某元素

class Fib(object):

    def __getitem__(self, n):
        # 实现索引
        if isinstance(n, int):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a

        # 实现切片
        if isinstance(n, slice):
            start = n.start
            stop = n.stop
            if start == None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b

            return L


f = Fib()
print(f[0])
print(f[10])
print(f[0:5])

print(f[:7])

没有对step参数做处理,也没有对负数作处理,所以,要正确实现一个__getitem__()还是有很多工作要做的。

此外,如果把对象看成dict__getitem__()的参数也可能是一个可以作key的object,例如str

与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

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

__getattr__:动态返回一个属性,适用于API调用等位置会改变的环境

只有在没有找到属性的情况下,才调用__getattr__,已有的属性不会在__getattr__中查找

class Chain(object):

    count=0
    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        Chain.count+=1
        print(Chain.count)
        print('%s/%s' % (self._path, path))
        return Chain('%s/%s' % (self._path, path))


    def __str__(self):
        return self._path

    __repr__ = __str__
1 /status
2 /status/user
3 /status/user/timeline
4 /status/user/timeline/list

得到的就/status/user/timeline/list。理解的关键点有三个:1.Chain没有status属性,会调用__getattr__函数。2.该函数会返回一个Chain类实例,输入的path是''/status,设为Chain2;3.Chain2没有user属性,重复1的步骤

__call__:直接在实例本身上调用方法

https://www.cnblogs.com/fengmk2/archive/2008/04/21/1163766.html

*args表示任何多个无名参数,它是一个tuple;**kwargs表示关键字参数,它是一个dict。并且同时使用*args和**kwargs时,必须*args参数列要在**kwargs前,像foo(a=1, b='2', c=3, a', 1, None, )这样调用的话,会提示语法错误“SyntaxError: non-keyword arg after keyword arg”。

 def kw_dict(**kwargs):
        return kwargs
    print kw_dict(a=1,b=2,c=3) == {'a':1, 'b':2, 'c':3}

其实python中就带有dict类,使用dict(a=1,b=2,c=3)即可创建一个字典了

class Student(object):
    count=0

    def __init__(self,name):
        self.name=name
        Student.count+=1

    def __call__(self,*args):
        print('My name is %s%s' % (self.name,args[0]))

    def __str__(self):
        return 'Screen object (name:%s)' % self.name
    __repr__=__str__
s=Student('yry')
s(1)
My name is yry1

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

我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例。

使用枚举类:定义常量

策略:为枚举类型定义一个class类型,每个常量都是class的唯一实例。Enum来实现

Month=Enum('Month',('Jan','Feb','Mar'))
for name,member in Month.__members__.items():
    print(name,"=>",member,',',member.value)
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
创建了Month类,Jane是一个对象,可以直接使用Month.Jan来引用一个常量
value属性是自动赋给成员的int常量,默认从1开始

精确地控制枚举类型

@unique
class Weekday(Enum):
    Sun=0
    Mon=1
d1=Weekday.Mon
print(d1)
print(Weekday(0))

@unique装饰器检查没有重复项,可以用名字来访问,也可以用value来访问

class Student(object):
    count=0

    def __init__(self,name,gender):
        self.name=name
        if gender in Gender:    #评论区也有人用isinstance
            self.gender=gender
        else:
            raise ValueError('Invalid')
        Student.count+=1

    def __call__(self,*args):
        print('My name is %s%s' % (self.name,args[0]))

    def __str__(self):
        return 'Screen object (name:%s)' % self.name
    __repr__=__str__
s=Student('yry',Gender.Male)
if s.gender==Gender.Male:
    print('Y')

使用元类

type() 通过type函数创建类/查看一个类型或变量类型

动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。

class Hello(object):
    def hello(self, name='world'):
        print('Hello, %s.' % name)

Hello本质上就是type类,它的实例是Hello类

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

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

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

metaclass  跳过

猜你喜欢

转载自blog.csdn.net/iroy33/article/details/102543308