python学习(八)定制类和枚举

定制类

python定制类主要是实现特定功能,通过在类中定义特定的函数完成特定的功能。

1
2
3
4
5
6
class Student(object):
def __init__(self, name):
self.name =name

student = Student("lilei")
print(student)

__str__用法

1
2
3
4
5
6
7
8
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return ("self name is %s" %(self.name))

student2 = Student("hanmeimei")
print(student2)

实现__str__函数,可以在print类对象时打印指定信息

通过实现__iter____next__同样可以使类对象产生可迭代序列,下面实现了斐波那契数列

1
2
3
4
5
6
7
8
9
10
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
if self.a > 30:
raise StopIteration()
return self.a

__iter__返回一个可迭代对象,这里我们返回了Fib自己的对象。
__next__是根据迭代器返回每一个迭代的序列,这里实现了运算规则,返回对象的a成员。
打印输出

1
2
for n in Fib():
print(n)

__getitem__用法

可以实现__getitem__函数,这样就可以按照索引访问类对象中迭代元素了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class OddNum(object):
def __init__(self):
self.num = -1
def __iter__(self):
return self
def __next__(self):
self.num = self.num +2
if self.num > 10:
raise StopIteration()
return self.num

def __getitem__(self,n):
temp = 1
for i in range(n):
temp += 2
return temp
1
2
3
4
5
for n in OddNum():
print(n)

oddnum = OddNum()
print(oddnum[3])

可以进一步完善OddNum类的__getitem__函数,使其支持切片处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def __getitem__(self, n):
if isinstance(n ,int):
temp =1
for i in range(n):
temp +=2
return temp
if isinstance(n, slice):
start = n.start
end = n.stop
if start is None:
start = 0
tempList = []
temp = 1
for i in range(end):
if i >= start:
temp += 2
tempList.append(temp)
return tempList

可通过如下方式调用
print(oddnum[1:4])

__getattr__用法

通过实现__getattr__函数,可以在类对象中没有某个属性时,自动调用__getattr__函数
实现__call__函数,可以实现类对象的函数式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def __getattr__(self,attr):
if attr == 'name':
return 'OddNum'
if attr == 'data':
return lambda:self.num
raise AttributeError('\'OddNum\' object has no attribute \'%s\'' %attr)
def __call__(self):
return "My name is OddNum!!"
```

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

``` python
print(oddnum.name)
print(oddnum.data)
#没有func函数会抛出异常
#print(oddnum.func)
#可以直接通过oddnum()函数式调用
print(oddnum())

定制类案例

下面是一个链式转化例子,用到了这些特定函数

1
2
3
4
5
6
7
8
9
10
11
class Chain(object):
def __init__(self, path=''):
self.path = path
def __getattr__(self,attr):
return Chain('%s/%s'%(self.path, attr))
def users(self, users):
return Chain('%s/users/%s' %(self.path, users))
def __str__(self):
return self.path
__repr__ = __str__
print(Chain().users('michael').repos)

__repr__表示一个对象的字符串显示,当我们打印这个对象时,如果没有实现__str__函数,则会调用__repr__函数。
另外,当我们打印datetime对象时,可以看到__repr__相比__str__目标更具有准确性,__str__目标在于可读性。
另外,在迭代器迭代打印的时候默认调用的是对象的__repr__函数。
所以在我们实现了__str__方法后,基本也会将其赋值给__repr__函数。
具体的参考连接https://www.cnblogs.com/miaoning/p/11399575.html
因为我们定义了__getattr__函数,所以当属性不存在时就会构造一个新的对象返回,返回的对象拼接了之前的path信息和attr属性。
我们定义了users函数,这样可以调用users函数了。但是我们可以用更简洁的办法实现各种函数的调用而不用定义他们,就是通过实现__call__函数,这样就可以实现链式调用。

1
2
3
4
5
6
7
8
9
10
11
12
class Chain(object):
def __init__(self, path=''):
self.path = path
def __getattr__(self,attr):
return Chain('%s/%s'%(self.path, attr))
def __call__(self, param):
return Chain('%s/%s'%(self.path, param))
def __str__(self):
return self.path
__repr__ = __str__

print(Chain().get.users('michael').group('doctor').repos)

当调用users(‘michael’)时,因为没有定义users函数,所以会调用__getattr__函数,进而构造一个新的Chain对象。因为实现了__call__函数,所以这个对象可以被当作函数来使用,也就是Chain(‘michael’)的方式。

enum枚举

python同样支持枚举操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from enum  import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec') )
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)

from enum import unique
@unique
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6

for name , member in Weekday.__members__.items():
print(name, '=>', member, ',', member.value)