1、动态语言的一个特点就是允许给实例绑定任意的方法和变量,而静态语言(例如Java)必须事先将属性方法写进类里。
给实例绑定变量:
>>> class Student: ... pass ... >>> s = Student() >>> s.name = 'Lily' >>> s.name 'Lily'
给实例绑定方法需要借助types模块的MethodType方法:
>>> def setAge(self, age): ... self.age = age ... >>> from types import MethodType >>> s.setAge = MethodType(setAge, s) >>> s.setAge(5) >>> s.age 5
还可以给class绑定方法,使得方法对所有实例都有效:
>>> Student.setAge = setAge
2、万一需要限制属性怎么办?即只允许类的实例添加有限个属性。
这个时候可以使用一个特殊的类变量__slots__:
>>> class Employee(object): ... __slots__ = ('name', 'salary') ... >>> e = Employee() >>> e.name = 'Wang fang' >>> e.salary = 18000 >>> e.age = 18 # age无法被添加为实例的属性! Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Employee' object has no attribute 'age'
但是需要注意的是,__slots__对继承的子类是不起作用的!
1、简而言之,@property就是一个类的方法的“装饰器”,添加在类的方法名的上一行,作用是把一个方法变成属性调用(即允许s.field这种访问方式),又能进行适当的参数检查。一个简单的例子:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- class Movie(object): # 把一个getter方法变成属性 @property def title(self): return self._title # 不能和装饰器@title.setter重名,所以使用_title @title.setter def title(self, title): if not isinstance(title, str): raise ValueError('title must be a str!') self._title = title # 类测试 if __name__ == '__main__': m = Movie() # m.title = 123 m.title = 'Tokyo Ghoul' print(m.title)
如果输入的title不是str类型,则会输出:
Traceback (most recent call last): File "D:labs est.py", line 20, in <module> File "D:labs est.py", line 14, in title ValueError: title must be a str!
2、如果要将上面的title属性改为只读,只需要删去setter方法就可以了。
3、练习。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- class Screen(object): @property def width(self): return self._width @width.setter def width(self, width): if width <= 0: raise ValueError('width must larger than 0') self._width = width @property def height(self): return self._height @height.setter def height(self, height): if height <= 0: raise ValueError('height must larger than 0') self._height = height @property def area(self): return self._width * self._height
测试结果:
>>> from test import Screen >>> s = Screen() >>> s.width = 0 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "D:labs est.py", line 13, in width raise ValueError('width must larger than 0') ValueError: width must larger than 0 >>> s.width = 80 >>> s.height = 70 >>> s.area = 10 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: can't set attribute >>> s.area 5600
1、多重继承:类似于Java可以实现多个接口。(仅仅是类似,不等于!)
2、引用网友:第一个继承的是生父,后边的都是继父,继父的名字后面要加MixIn表明身份,如果方法有冲突,优先继承生父的。
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass
注:第一个是主线继承,后两个是增加的功能。
1、__str__()、__repr__()类似于Java中的toString()。前者用于s.print(object)、后者用于直接输入s时的输出。
def __str__(self): ... return 'Student object (name: %s)' % self.name
偷懒的写法:写完__str__()后,__repr__ = __str__
2、__iter__()、__next__()。简单的例子:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- class f(): def __init__(self): self.a = 0 def __iter__(self): return self # 返回一个迭代对象给for def __next__(self): # 实现一个next方法给for调用 self.a += 1 return self.a
4、__getitem__:让自定义对象表现得像list、str等类型一样。(可以进行索引、切片等操作)
class f(): def __getitem__(self, n): return n
>>> from test import f >>> f()[1] 1 >>> f()[3] 3
如果要支持切片:
class f(): def __getitem__(self, n): if isinstance(n, int): return n if isinstance(n, slice): start = n.start stop = n.stop if start == None: #例如[:3]默认为[0:3] start = 0 L = [] for x in range(start, stop): L.append(x) return L
>>> f()[1:3]
[1, 2]
5、__getattr__:attr是attribute的简写。当实例本身不具备某个属性,却又强行被调用的时候会调用该函数。
class f(): def __init__(self): self.a = 1111 self.b = 33333 def __getattr__(self, attr): return "i had not attribute " + attr + "!"
>>> f().a 1111 >>> f().b 33333 >>> f().c 'i had not attribute c!'
当然也可以返回函数。
利用完全动态的__getattr__
,可以写出一个链式调用。。。
6、__call__。
1)判断一个变量是对象还是函数:callable()。
2)把对象变成函数的方法:重写__call__方法。
such:
class f: def __call__(self, a, b): return a + b
>>> sum = f() >>> sum(1, 2) 3
更多python的定制方法参考python官方文档。
在Python中枚举类型也可以被视为一个类。
1、有两种方式可以创建这种类。
1)赋值法。
>>> from enum import Enum >>> Colors = Enum('Colors', ('RED', 'YELLOW', 'BLUE'))
自然的操作,尝试打印一个Enum常量:
>>> Colors.RED
<Colors.RED: 1>
2)继承Enum。
from enum import Enum, unique
@unique class Colors(Enum): RED = 10086 YELLOW = 111 BLUE = 2222
尝试打印:
>>> from test import Colors >>> Colors.RED <Colors.RED: 10086>
10086即是RED的value。ps:@unique装饰器可以帮助检查有没有重复值。
2、枚举类用于逻辑关系运算 & 打印枚举类型变量
>>> myColor = Colors.BLUE >>> myColor == Colors.RED False >>> print(myColor) Colors.BLUE
1、Python中的type()。
type()是用来干嘛的呢?根据字面上的意思很容易想到,可以通过type来输出Python中各种构件的类型,例如:
>>> type(1) <class 'int'> >>> type('hi python') <class 'str'> >>> type(lambda x: x) <class 'function'>
尝试传入类名、对象会发现:
>>> class MyClass(object): ... pass ... >>> type(MyClass) <class 'type'> >>> type(MyClass()) <class '__main__.MyClass'>
MyClass()对象的类型是MyClass,而MyClass类的类型是type!
这就说明,MyClass本身也可以被看作是由type创建的对象,而MyClass这个对象又具备创建对象的能力。
因此,type()除了可以用于查看各种Python构件的类型外,还可以用来创建类。
实际上在Python的解释器内部创建类的工作都是自动调用type()完成的。那么如何手动调用type()呢?
只需要将传统的class创建方式映射到type()上来就可以了。
一个传统的dog:
class Dog(object): def __init__(self, name, age): self.name = name self.age = age def run(self): print(self.name + ' run! ')
>>> from test import Dog >>> i = Dog('Huahua', 2) >>> i.run() Huahua run!
一个type()创建的dog类:
# 先把组件写好 # 构造器 def f1(self, name, age): self.name = name self.age = age # run方法 def f2(self): print(self.name + ' run! ') # 组装成type() Dog = type('Dog', (object,), dict(__init__=f1, run=f2))
测试:
>>> def f1(self, name, age): ... self.name = name ... self.age = age ... >>> def f2(self): ... print(self.name + ' run! ') ... >>> Dog = type('Dog', (object,), dict(__init__=f1, run=f2)) >>> i = Dog('Lele', 3) >>> i.run() Lele run!
为什么使用type()而不是class来创建类呢?因为type()更具动态性。
2、元类。参考博客:深刻理解Python中的元类
为什么使用metaclass?和使用type一样,为了创建基于上下文的类。
什么是metaclass?type就是Python内置的一个metaclass!
一个使用metaclass会让事情变得简单的例子:数据库的GUI。(用type做不了,非metaclass不可)
定义metaclass
用metaclass创建类