DataWhale7月学习——Python入门
Task7:类、对象与魔法方法
本节我们初步学习Python语法中的类、对象和魔法方法。文章给出了一些重点知识的.py程序便于读者深入理解。本文的程序编写基于Python3.0+,安装环境使用的是PyCharm。
类与对象
对象=属性+方法
对象是类的实例。
类主要定义对象的结构,以类为模板创建对象。类不但包含方法定义,还包含所有实例共享的数据。
1.封装
利用关键字class定义Python中的类,关键字后面紧跟类的名称、分号和类的实现
class Turtle: # Python中的类名约定以⼤写字⺟开头
"""关于类的⼀个简单例⼦"""
# 属性
color = 'green'
weight = 10
legs = 4
shell = True
mouth = '⼤嘴'
# ⽅法
def climb(self):
print('我正在很努⼒的向前爬...')
def run(self):
print('我正在⻜快的向前跑...')
def bite(self):
print('咬死你咬死你!!')
def eat(self):
print('有得吃,真满⾜...')
def sleep(self):
print('困了,睡了,晚安,zzz')
tt = Turtle()
print(tt)
# <__main__.Turtle object at 0x0000007C32D67F98>
print(type(tt))
# <class '__main__.Turtle'>
print(tt.__class__)
# <class '__main__.Turtle'>
print(tt.__class__.__name__)
# Turtle
tt.climb()
# 我正在很努⼒的向前爬...
tt.run()
# 我正在⻜快的向前跑...
tt.bite()
# 咬死你咬死你!!
# Python类也是对象。它们是type的实例
print(type(Turtle))
# <class 'type'>
2.继承
子类自动共享父类之间的数据和方法机制
class MyList(list):
pass
lst = MyList([1, 5, 2, 7, 8])
lst.append(9)
lst.sort()
print(lst)
# [1, 2, 5, 7, 8, 9]
3.多态
不同对象对同一方法响应不同的行动
class Animal:
def run(self):
raise AttributeError('⼦类必须实现这个⽅法')
class People(Animal):
def run(self):
print('⼈正在⾛')
class Pig(Animal):
def run(self):
print('pig is walking')
class Dog(Animal):
def run(self):
print('dog is running')
def func(animal):
animal.run()
func(Pig())
# pig is walking
类方法与普通函数只有一个特别的区别——他们必须有一个额外的第一个参数名称(self),在调用时,无需提供与参数self相对应的参数
class Ball:
def setName(self, name):
self.name = name
def kick(self):
print("我叫%s,该死的,谁踢我..." % self.name)
a = Ball()
a.setName("球A")
b = Ball()
b.setName("球B")
c = Ball()
c.setName("球C")
a.kick()
# 我叫球A,该死的,谁踢我...
b.kick()
# 我叫球B,该死的,谁踢我...
Python的魔法方法
Python中类有一个名为__init__(self[, param1, param2…])的魔法方法,该方法在类实例化时会自动调用
class Ball:
def __init__(self, name):
self.name = name
def kick(self):
print("我叫%s,该死的,谁踢我..." % self.name)
a = Ball("球A")
b = Ball("球B")
c = Ball("球C")
a.kick()
# 我叫球A,该死的,谁踢我...
b.kick()
# 我叫球B,该死的,谁踢我...
公有和私有
在Python中定义私有变量只需要在变量名或函数名前加上两个下划线,那么这个函数或变量就会私有了
1.类的私有属性
class JustCounter:
__secretCount = 0 # 私有变量
publicCount = 0 # 公开变量
def count(self):
self.__secretCount += 1
self.publicCount += 1
print(self.__secretCount)
counter = JustCounter()
counter.count() # 1
counter.count() # 2
print(counter.publicCount) # 2
print(counter._JustCounter__secretCount) # 2 Python的私有为伪私有
print(counter.__secretCount)
# AttributeError: 'JustCounter' object has no attribute '__secretCount'
2.类的私有方法
class Site:
def __init__(self, name, url):
self.name = name # public
self.__url = url # private
def who(self):
print('name : ', self.name)
print('url : ', self.__url)
def __foo(self): # 私有⽅法
print('这是私有⽅法')
def foo(self): # 公共⽅法
print('这是公共⽅法')
self.__foo()
x = Site('⽼⻢的程序⼈⽣', 'https://blog.csdn.net/LSGO_MYP')
x.who()
# name : ⽼⻢的程序⼈⽣
# url : https://blog.csdn.net/LSGO_MYP
x.foo()
# 这是公共⽅法
# 这是私有⽅法
x.__foo()
# AttributeError: 'Site' object has no attribute '__foo'
继承
派生类的定义:
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
基类名BaseClassName必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模板
class DerivedClassName(modname.BaseClassName):
<statement-1>
.
.
.
<statement-N>
如果子类中定义与父类同名的方法或属性,则会覆盖父类对应的方法或属性
# 类定义
class people:
# 定义基本属性
name = ''
age = 0
# 定义私有属性,私有属性在类外部⽆法直接进⾏访问
__weight = 0
# 定义构造⽅法
def __init__(self, n, a, w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说: 我 %d 岁。" % (self.name, self.age))
# 单继承示例
class student(people):
grade = ''
def __init__(self, n, a, w, g):
# 调⽤⽗类的构函
people.__init__(self, n, a, w)
self.grade = g
# 覆写⽗类的⽅法
def speak(self):
print("%s 说: 我 %d 岁了,我在读 %d 年级" % (self.name, self.age, self.grade))
s = student('⼩⻢的程序⼈⽣', 10, 60, 3)
s.speak()
# ⼩马的程序⼈⽣ 说: 我 10 岁了,我在读 3 年级
class Fish:
def __init__(self):
self.x = r.randint(0, 10)
self.y = r.randint(0, 10)
def move(self):
self.x -= 1
print("我的位置", self.x, self.y)
class GoldFish(Fish): # ⾦⻥
pass
class Carp(Fish): # 鲤⻥
pass
class Salmon(Fish): # 三⽂⻥
pass
class Shark(Fish): # 鲨⻥
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print("吃货的梦想就是天天有得吃!")
self.hungry = False
else:
print("太撑了,吃不下了!")
self.hungry = True
g = GoldFish()
g.move() # 我的位置 9 4
s = Shark()
s.eat() # 吃货的梦想就是天天有得吃!
s.move()
# AttributeError: 'Shark' object has no attribute 'x'
调用未绑定的父类方法Fish.init(self)
class Shark(Fish): # 鲨⻥
def __init__(self):
Fish.__init__(self)
self.hungry = True
def eat(self):
if self.hungry:
print("吃货的梦想就是天天有得吃!")
self.hungry = False
else:
print("太撑了,吃不下了!")
self.hungry = True
使用super函数super().init()
class Shark(Fish): # 鲨⻥
def __init__(self):
super().__init__()
self.hungry = True
def eat(self):
if self.hungry:
print("吃货的梦想就是天天有得吃!")
self.hungry = False
else:
print("太撑了,吃不下了!")
self.hungry = True
多继承
注意圆括号中父类的顺序,若父类有相同的类名,而在子类使用时未指定,Python从左到右搜索,即在子类中未找到时,从左到右查找父类中是否包含方法
# 类定义
class People:
# 定义基本属性
name = ''
age = 0
# 定义私有属性,私有属性在类外部⽆法直接进⾏访问
__weight = 0
# 定义构造⽅法
def __init__(self, n, a, w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说: 我 %d 岁。" % (self.name, self.age))
# 单继承示例
class Student(People):
grade = ''
def __init__(self, n, a, w, g):
# 调⽤⽗类的构函
People.__init__(self, n, a, w)
self.grade = g
# 覆写⽗类的⽅法
def speak(self):
print("%s 说: 我 %d 岁了,我在读 %d 年级" % (self.name, self.age, self.grade))
# 另⼀个类,多重继承之前的准备
class Speaker:
topic = ''
name = ''
def __init__(self, n, t):
self.name = n
self.topic = t
def speak(self):
print("我叫 %s,我是⼀个演说家,我演讲的主题是 %s" % (self.name, self.topic))
# 多重继承
class Sample01(Speaker, Student):
a = ''
def __init__(self, n, a, w, g, t):
Student.__init__(self, n, a, w, g)
Speaker.__init__(self, n, t)
test = Sample01("Tim", 25, 80, 4, "Python")
test.speak() # ⽅法名同,默认调⽤的是在括号中排前地⽗类的⽅法
# 我叫 Tim,我是⼀个演说家,我演讲的主题是 Python
class Sample02(Student, Speaker):
a = ''
def __init__(self, n, a, w, g, t):
Student.__init__(self, n, a, w, g)
Speaker.__init__(self, n, t)
test = Sample02("Tim", 25, 80, 4, "Python")
test.speak() # ⽅法名同,默认调⽤的是在括号中排前地⽗类的⽅法
# Tim 说: 我 25 岁了,我在读 4 年级
组合
class Turtle:
def __init__(self, x):
self.num = x
class Fish:
def __init__(self, x):
self.num = x
class Pool:
def __init__(self, x, y):
self.turtle = Turtle(x)
self.fish = Fish(y)
def print_num(self):
print("⽔池⾥⾯有乌⻳%s只,⼩⻥%s条" % (self.turtle.num, self.fish.num))
p = Pool(2, 3)
p.print_num()
# ⽔池⾥⾯有乌⻳2只,⼩⻥3条
类、类对象和实例对象
类对象:创建一个类,其实也是一个对象在内存开辟了一块空间,类对象只有一个
实例对象:实例化类创建的对象,实例对象可以有多个
类属性:类里面方法外面定义的变量,类属性所属类对象并且对个实例对象之间公用一个类属性
实例属性:实例属性和具体的某个实例对象有关系,并且一个实例对象和另外一个实例对象不公用属性
注意:属性与方法名相同时,属性会覆盖方法
# 类对象
class A(object):
pass
# 实例化对象 a、b、c都属于实例对象。
a = A()
b = A()
c = A()
class A():
a = xx #类属性
def __init__(self):
A.a = xx #使⽤类属性可以通过 (类名.类属性)调⽤
class 类名():
__init__(self):
self.name = xx #实例属性
类属性和实例属性区别:
1.类属性:类外面,可以通过实例对象.类属性和类名.类属性进行调用;类里面,可以通过self.类属性和类名.类属性进行调用
2.实例对象:类外面,可以通过实例对象.实例属性调用;类里面self.实例属性调用
3.实例对象相当于局部变量,出了这个类或者类的实例对象,就没有作用了;类对象相当于全局变量,可以和这个类的所有实例对象共享
# 创建类对象
class Test(object):
class_attr = 100 # 类属性
def __init__(self):
self.sl_attr = 100 # 实例属性
def func(self):
print('类对象.类属性的值:', Test.class_attr) # 调⽤类属性
print('self.类属性的值', self.class_attr) # 相当于把类属性 变成实例属性
print('self.实例属性的值', self.sl_attr) # 调⽤实例属性
a = Test()
a.func()
# 类对象.类属性的值: 100
# self.类属性的值 100
# self.实例属性的值 100
b = Test()
b.func()
# 类对象.类属性的值: 100
# self.类属性的值 100
# self.实例属性的值 100
a.class_attr = 200
a.sl_attr = 200
a.func()
# 类对象.类属性的值: 100
# self.类属性的值 200
# self.实例属性的值 200
b.func()
# 类对象.类属性的值: 100
# self.类属性的值 100
# self.实例属性的值 100
Test.class_attr = 300
a.func()
# 类对象.类属性的值: 300
# self.类属性的值 200
# self.实例属性的值 200
b.func()
# 类对象.类属性的值: 300
# self.类属性的值 300
# self.实例属性的值 100
class A:
def x(self):
print('x_man')
aa = A()
aa.x() # x_man
aa.x = 1
print(aa.x) # 1
aa.x()
# TypeError: 'int' object is not callable
绑定
Python严格要求方法必须有实例才能被调用。Python对象的数据属性通常存储在名为.__ dict__的字典中,我们直接访问__dict__或者利用Python中的内置函数vars()获取.__ dict__
class CC:
def setXY(self, x, y):
self.x = x
self.y = y
def printXY(self):
print(self.x, self.y)
dd = CC()
print(dd.__dict__)
# {}
print(vars(dd))
# {}
print(CC.__dict__)
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000C3473DA048>, 'printXY':
<function CC.printXY at 0x000000C3473C4F28>, '__dict__': <attribute '__dict__' of 'CC' objects>,
'__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
dd.setXY(4, 5)
print(dd.__dict__)
# {'x': 4, 'y': 5}
print(vars(CC))
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY':
<function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>,
'__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
print(CC.__dict__)
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY':
<function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>,
'__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}
一些内置函数
1.issubclass(class, classinfo)用于判断参数class是否是类型参数classinfo的子类;
classinfo可以是类对象的元组,只要class是其中任何一个候选类的子类,则返回True
class A:
pass
class B(A):
pass
print(issubclass(B, A)) # True
print(issubclass(B, B)) # True
print(issubclass(A, B)) # False
print(issubclass(B, object)) # True
2.isinstance(object, classinfo)用于判断一个对象是否是一个已知类型,类似于type();type()不会认为子类是一种父类类型,不考虑继承关系;isinstance()会认为子类是一种父类类型,考虑继承关系。
如果第一个参数不是对象,则返回False;如果第二个参数不是类或者由类对象组成的元组,则返回
TypeError异常
a = 2
print(isinstance(a, int)) # True
print(isinstance(a, str)) # False
print(isinstance(a, (str, int, list))) # True
class A:
pass
class B(A):
pass
print(isinstance(A(), A)) # True
print(type(A()) == A) # True
print(isinstance(B(), A)) # True
print(type(B()) == A) # False
3.hasattr(object, name)用于判断对象是否包含对应的属性
class Coordinate:
x = 10
y = -5
z = 0
point1 = Coordinate()
print(hasattr(point1, 'x')) # True
print(hasattr(point1, 'y')) # True
print(hasattr(point1, 'z')) # True
print(hasattr(point1, 'no')) # False
4.getattr(object, name[, default])用于返回一个对象属性值
class A(object):
bar = 1
a = A()
print(getattr(a, 'bar')) # 1
print(getattr(a, 'bar2', 3)) # 3
print(getattr(a, 'bar2'))
# AttributeError: 'A' object has no attribute 'bar2'
class A(object):
def set(self, a, b):
x = a
a = b
b = x
print(a, b)
a = A()
c = getattr(a, 'set')
c(a='1', b='2') # 2 1
5.setattr(object, name, value)对应函数getattr(),用于设置属性值,该属性不一定存在
class A(object):
bar = 1
a = A()
print(getattr(a, 'bar')) # 1
setattr(a, 'bar', 5)
print(a.bar) # 5
setattr(a, "age", 28)
print(a.age) # 28
6.delattr(object, name)用于删除属性
class Coordinate:
x = 10
y = -5
z = 0
point1 = Coordinate()
print('x = ', point1.x) # x = 10
print('y = ', point1.y) # y = -5
print('z = ', point1.z) # z = 0
delattr(Coordinate, 'z')
print('--删除 z 属性后--') # --删除 z 属性后--
print('x = ', point1.x) # x = 10
print('y = ', point1.y) # y = -5
# 触发错误
print('z = ', point1.z)
# AttributeError: 'Coordinate' object has no attribute 'z'
7.class property([fget[, fset[, fdel[, doc]]]])用于在新类中返回属性值
class C(object):
def __init__(self):
self.__x = None
def getx(self):
return self.__x
def setx(self, value):
self.__x = value
def delx(self):
del self.__x
x = property(getx, setx, delx, "I'm the 'x' property.")
cc = C()
cc.x = 2
print(cc.x) # 2
魔法方法
魔法方法总是被双下划线包围:init
魔法方法总是面向Python的一切,总能够在适当的时候被自动调用
魔法方法的第一个参数应该为cls(类方法)或者self(实例方法)
基本的魔法方法
1.init(self[, …])
构造器,当一个实例被创建的时候调用的初始化方法
class Rectangle:
def __init__(self, x, y):
self.x = x
self.y = y
def getPeri(self):
return (self.x + self.y) * 2
def getArea(self):
return self.x * self.y
rect = Rectangle(4, 5)
print(rect.getPeri()) # 18
print(rect.getArea()) # 20
2.new(cls[, …])
new__是在一个对象实例化的时候调用的第一个方法,在调用__init__初始化前,先调用__new
new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供,后面的参数传递给__init
new__对当前类进行了实例化,并将实例返回,传给__init__的self。但是执行了__new,并不一定会进入__init__,只有__new__返回了,当前cls的实例,当前类的__init__才进入
class A(object):
def __init__(self, value):
print("into A __init__")
self.value = value
def __new__(cls, *args, **kwargs):
print("into A __new__")
print(cls)
return object.__new__(cls)
class B(A):
def __init__(self, value):
print("into B __init__")
self.value = value
def __new__(cls, *args, **kwargs):
print("into B __new__")
print(cls)
return super().__new__(cls, *args, **kwargs)
b = B(10)
# 结果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.B'>
# into B __init__
class A(object):
def __init__(self, value):
print("into A __init__")
self.value = value
def __new__(cls, *args, **kwargs):
print("into A __new__")
print(cls)
return object.__new__(cls)
class B(A):
def __init__(self, value):
print("into B __init__")
self.value = value
def __new__(cls, *args, **kwargs):
print("into B __new__")
print(cls)
return super().__new__(A, *args, **kwargs) # 改动了cls变为A
b = B(10)
# 结果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.A'>
若__new__没有正确返回当前类cls的实例,那__init__是不会被调用的,即使是父类的实例也不行,将没有__init__被调用
可利用__new__实现单例模式
class Earth:
pass
a = Earth()
print(id(a)) # 260728291456
b = Earth()
print(id(b)) # 260728291624
class Earth:
__instance = None # 定义⼀个类属性做判断
def __new__(cls):
if cls.__instance is None:
cls.__instance = object.__new__(cls)
return cls.__instance
else:
return cls.__instance
a = Earth()
print(id(a)) # 512320401648
b = Earth()
print(id(b)) # 512320401648
__new__方法主要是当继承一些不可变的class时(如int, str, tuple),提供给你一个自定义这些类的实例化过程的途径
class CapStr(str):
def __new__(cls, string):
string = string.upper()
return str.__new__(cls, string)
a = CapStr("i love lsgogroup")
print(a) # I LOVE LSGOGROUP
3.del(self)
析构器,当一个对象将要被系统回收时调用的方法
class C(object):
def __init__(self):
print('into C __init__')
def __del__(self):
print('into C __del__')
c1 = C()
# into C __init__
c2 = c1
c3 = c2
del c3
del c2
del c1
# into C __del__
4.str__和__repr
str(self):
当你打印一个对象时,触发__str__;当你使用%s格式的时候,触发__str__;str强制转换类型的时候,触发__str__
repr(self):
repr是str的备胎;有__str__的时候执行__str__,没有__str__的之后执行__repr__;repr(obj)内置函数对应的结果是__repr__的返回值;当你使用%r格式化的时候触发__repr__
class Cat:
"""定义⼀个猫类"""
def __init__(self, new_name, new_age):
"""在创建完对象之后 会⾃动调⽤, 它完成对象的初始化的功能"""
self.name = new_name
self.age = new_age
def __str__(self):
"""返回⼀个对象的描述信息"""
return "名字是:%s , 年龄是:%d" % (self.name, self.age)
def __repr__(self):
"""返回⼀个对象的描述信息"""
return "Cat:(%s,%d)" % (self.name, self.age)
def eat(self):
print("%s在吃⻥...." % self.name)
def drink(self):
print("%s在喝可乐..." % self.name)
def introduce(self):
print("名字是:%s, 年龄是:%d" % (self.name, self.age))
# 创建了⼀个对象
tom = Cat("汤姆", 30)
print(tom) # 名字是:汤姆 , 年龄是:30
print(str(tom)) # 名字是:汤姆 , 年龄是:30
print(repr(tom)) # Cat:(汤姆,30)
tom.eat() # 汤姆在吃⻥....
tom.introduce() # 名字是:汤姆, 年龄是:30
str(self)的返回结果可读性强;repr(self)的返回结果更准确
import datetime
today = datetime.date.today()
print(str(today)) # 2019-10-11
print(repr(today)) # datetime.date(2019, 10, 11)
print('%s' %today) # 2019-10-11
print('%r' %today) # datetime.date(2019, 10, 11)
算术运算符
类型工厂函数:不通过类而是通过函数来创建对象
class C:
pass
print(type(len)) # <class 'builtin_function_or_method'>
print(type(dir)) # <class 'builtin_function_or_method'>
print(type(int)) # <class 'type'>
print(type(list)) # <class 'type'>
print(type(tuple)) # <class 'type'>
print(type(C)) # <class 'type'>
print(int('123')) # 123
# 这个例⼦中list⼯⼚函数把⼀个元祖对象加⼯成了⼀个列表对象。
print(list((1, 2, 3))) # [1, 2, 3]
add(self, other)定义加法行为+
sub(self, other)定义减法行为-
mul(self, other)定义乘法行为*
truediv(self, other)定义真除法行为/
floordiv(self, other)定义整数除法行为//
mod(self, other)定义取模算法的行为%
divmod(self, other)定义当被divmod()调用时的行为
divmod(a, b)把除数和余数运算结合起来,返回一个包含商和余数的元组(a // b, a % b)
pow(self, other[, module])定义当被power()调用或**运算时的行为
lshift(self, other)定义按位左移位的行为:<<
rshift(self, other)定义按位右移位的行为:>>
and(self, other)定义按位与操作的行为:&
xor(self, other)定义按位异或操作的行为:^
or(self, other)定义按位或操作的行为:|
class MyClass:
def __init__(self, height, weight):
self.height = height
self.weight = weight
# 两个对象的⻓相加,宽不变.返回⼀个新的类
def __add__(self, others):
return MyClass(self.height + others.height, self.weight + others.weight)
# 两个对象的宽相减,⻓不变.返回⼀个新的类
def __sub__(self, others):
return MyClass(self.height - others.height, self.weight - others.weight)
# 说⼀下⾃⼰的参数
def intro(self):
print("⾼为", self.height, " 重为", self.weight)
def main():
a = MyClass(height=10, weight=5)
a.intro()
b = MyClass(height=20, weight=10)
b.intro()
c = b - a
c.intro()
d = a + b
d.intro()
if __name__ == '__main__':
main()
# ⾼为 10 重为 5
# ⾼为 20 重为 10
# ⾼为 10 重为 5
# ⾼为 30 重为 15
print(divmod(7, 2)) # (3, 1)
print(divmod(8, 2)) # (4, 0)
反算术运算符
与算数运算符不同的是多了个‘r’,当文件左操作不支持相应的操作时调用
radd(self, other)定义加法运算+
rsub(self, other)定义减法运算-
rmul(self, other)定义乘法运算*
rtruediv(self, other)定义真除法运算/
a + b 这里加数是a,被加数是b,因此是a主动,反运算就是如果a对象的__add__()没有实现或者不支持相应的操作,那么Python就会调用b的__radd__()方法
class Nint(int):
def __radd__(self, other):
return int.__sub__(other, self) # 注意 self 在后⾯
a = Nint(5)
b = Nint(3)
print(a + b) # 8
print(1 + b) # -2
属性访问
getattr,getattribute,setattr__和__delattr
getattr(self, name):定义当用户试图获取一个不存在的属性时的行为
getattribute(self, name):定义当该类的属性被访问时的行为(先调用该方法,查看是否存在该属性,若不存在,接着去调用__getattr__)
setattr(self, name, value):定义当一个属性被设置时的行为
delattr(self, name):定义当一个属性被删除时的行为
class C:
def __getattribute__(self, item):
print('__getattribute__')
return super().__getattribute__(item)
def __getattr__(self, item):
print('__getattr__')
def __setattr__(self, key, value):
print('__setattr__')
super().__setattr__(key, value)
def __delattr__(self, item):
print('__delattr__')
super().__delattr__(item)
c = C()
c.x
# __getattribute__
# __getattr__
c.x = 1
# __setattr__
del c.x
# __delattr__
描述符
描述符就是将某种特殊类型的类的实例指派给另一个类的属性
get(self, instance, owner)用于访问属性,它返回属性的值
set(self, instance, value)将在属性分配操作中调用,不返回任何内容
del(self, instance)控制删除操作,不返回任何内容
class MyDecriptor:
def __get__(self, instance, owner):
print('__get__', self, instance, owner)
def __set__(self, instance, value):
print('__set__', self, instance, value)
def __delete__(self, instance):
print('__delete__', self, instance)
class Test:
x = MyDecriptor()
t = Test()
t.x
# __get__ <__main__.MyDecriptor object at 0x000000CEAAEB6B00> <__main__.Test object at
0x000000CEABDC0898> <class '__main__.Test'>
t.x = 'x-man'
# __set__ <__main__.MyDecriptor object at 0x00000023687C6B00> <__main__.Test object at
0x00000023696B0940> x-man
del t.x
# __delete__ <__main__.MyDecriptor object at 0x000000EC9B160A90> <__main__.Test object at
0x000000EC9B160B38>
定制序列
容器类型的协议
如果希望定制的容器是不可变的,只需要定义__len__()和__getitem__()
如果希望定制的容易是可变的,还需要的定义__setitem__()和__delitem__()两个方法
例:编写一个不可变的自定义列表,要求记录列表中每个元素被访问的次数
class CountList:
def __init__(self, *args):
self.values = [x for x in args]
self.count = {}.fromkeys(range(len(self.values)), 0)
def __len__(self):
return len(self.values)
def __getitem__(self, item):
self.count[item] += 1
return self.values[item]
c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1]) # 3
print(c2[2]) # 6
print(c1[1] + c2[1]) # 7
print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
print(c2.count)
# {0: 0, 1: 1, 2: 1, 3: 0, 4: 0}
len(self)定义当被len()调用时的行为(返回容器中元素的个数)
getitem(self, key)定义获取元素的行为,相当于self[key]
setitem(self, key, value)定义设置容器中指定元素的行为,相当于self[key] = value
delitem(self, key)定义删除容器中指定元素的行为,相当于del self[key]
例:编写一个可改变的自定义列表,要求记录列表中每个元素被访问的次数
class CountList:
def __init__(self, *args):
self.values = [x for x in args]
self.count = {}.fromkeys(range(len(self.values)), 0)
def __len__(self):
return len(self.values)
def __getitem__(self, item):
self.count[item] += 1
return self.values[item]
def __setitem__(self, key, value):
self.values[key] = value
def __delitem__(self, key):
del self.values[key]
for i in range(0, len(self.values)):
if i >= key:
self.count[i] = self.count[i + 1]
self.count.pop(len(self.values))
c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1]) # 3
print(c2[2]) # 6
c2[2] = 12
print(c1[1] + c2[2]) # 15
print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
print(c2.count)
# {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}
del c1[1]
print(c1.count)
# {0: 0, 1: 0, 2: 0, 3: 0}
迭代器
迭代是Python最强大的功能之一,是访问集合元素的一种方式;
迭代器是一个可以记住遍历的位置的对象;
迭代器对象从集合的第一个元素开始访问,直到所有元素访问完结束;
迭代器只会向前不会后退;
字符串、列表或元组对象都可用于创建迭代器
迭代器有两个基本的方法:iter()和next()
iter(object)用来生成迭代器
next(iterator[, default])返回迭代器的下一个项目
iterator:可迭代对象
default:可选,用于设置在没有下一个元素时返回该默认值,如果不设置,又没有下一个元素则会触发StopIteration异常
links = {'B': '百度', 'A': '阿⾥', 'T': '腾讯'}
it = iter(links)
print(next(it)) # B
print(next(it)) # A
print(next(it)) # T
print(next(it)) # StopIteration
it = iter(links)
while True:
try:
each = next(it)
except StopIteration:
break
print(each)
# B
# A
# T
string = 'lsgogroup'
for c in string:
print(c)
'''
l
s
g
o
g
r
o
u
p
'''
for c in iter(string):
print(c)
links = {'B': '百度', 'A': '阿⾥', 'T': '腾讯'}
for each in links:
print('%s -> %s' % (each, links[each]))
'''
B -> 百度
A -> 阿⾥
T -> 腾讯
'''
for each in iter(links):
print('%s -> %s' % (each, links[each]))
把一个类作为一个迭代器使用需要在类中实现两个魔法方法__iter__()和__next__()
iter(self)定义当迭代容器中的行为,返回一个特殊的迭代器对象,这个迭代对象实现了__next__()方法并通过StopIteration异常标识迭代的完成
_next_()返回下一个迭代器对象
StopIteration异常用于标识迭代器的完成,防止出现无限循环的情况,在__next__()方法中我们可以设置在完成迭代次数后触发StopIteration异常来结束迭代
class Fibs:
def __init__(self, n=10):
self.a = 0
self.b = 1
self.n = n
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a > self.n:
raise StopIteration
return self.a
fibs = Fibs(100)
for each in fibs:
print(each, end=' ')
# 1 1 2 3 5 8 13 21 34 55 89
生成器
在Python中,使用了yield的函数称为生成器
生成器是一个返回迭代器的函数,只能用于迭代操作
在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行
调用一个生成器函数,返回的是一个迭代器对象
def myGen():
print('⽣成器执⾏!')
yield 1
yield 2
myG = myGen()
print(next(myG))
# ⽣成器执⾏!
# 1
print(next(myG)) # 2
print(next(myG)) # StopIteration
myG = myGen()
for each in myG:
print(each)
'''
⽣成器执⾏!
12
'''
def libs(n):
a = 0
b = 1
while True:
a, b = b, a + b
if a > n:
return
yield a
for each in libs(100):
print(each, end=' ')
# 1 1 2 3 5 8 13 21 34 55 89
练习题
类与对象
2.如何定义私有方法?
Python对于类的成员没有严格的访问控制限制,这与其他面相对对象语言有区别。关于私有属性和私有方法,有如下要点:
通常我们约定,两个下划线开头的属性是私有的(private)。其他为公共的(public);
类内部可以访问私有属性(方法);
类外部不能直接访问私有属性(方法);
类外部可以通过 ”_类名__私有属性(方法)名“ 访问私有属性(方法)
3.
class C:
def myFun():
print('Hello!')
c = C()
c.myFun()
错误原因:
myFun(self)
c=C() c.myFun()应和class C: 对齐
class Ticket:
def __init__(self, total_price=0, price=100):
self.price = price
self.total_price = total_price
# 统计票价函数
def cost_ticket(self):
day = int(input("您需要购买哪天的票?请输入1-7的正整数(1-5代表工作日,6-7代表周末):"))
if day <= 0 or day > 7:
errorDay = Exception("请检查您的购买日期!")
raise errorDay
adult = int(input("请输入需要购买大人的票数(张):"))
if adult < 0:
errorAdultTicket = Exception("请检查购买成人的票数!")
raise errorAdultTicket
child = int(input("请输入需要购买儿童的票数(张):"))
if child < 0:
errorChildTicket = Exception("请检查购买儿童的票数!")
raise errorChildTicket
if day in range(1, 6):
self.total_price = adult * self.price + child * self.price * 0.5
elif day in range(6, 8):
self.total_price = adult * self.price * 1.2 + child * self.price * 1.2 * 0.5
return self.total_price
try:
price_total = CostTicket().cost_ticket()
print("您需要支付{}元。".format(price_total))
except ValueError:
print("请输入正确的整数!")
except Exception as errorResult:
print(errorResult)
a=Ticket()
a.cost_ticket()
魔法方法
2.利用Python做一个简单的定时器类
import time as t
class MyTimer:
def __init__(self):
self.unit = ['年','月','天','小时','分钟','秒']
self.prompt = "未开始计时"
self.lasted = []
self.begin = 0
self.end = 0
def start(self):
self.begin = t.localtime()
self.prompt = "提示:请先调用stop开始计时"
print("计时开始......")
def stop(self):
if not self.begin:
print("提示:请先调用start()开始计时")
else:
self.end = t.localtime()
self._calc()
print("计时结束")
def _calc(self):
self.lasted = []
self.prompt = "总共运行了"
for index in range(6):
self.lasted.append(self.end[index] - self.begin[index])
if self.lasted[index]:
self.prompt += (str(self.lasted[index]) + self.unit[index])
self.begin = 0
self.end = 0
t1=MyTimer()
t1.prompt
t1.start()