1、以下类定义中哪些是类属性,哪些是实例属性?
class C:
num = 0 #类属性
def init(self):
self.x = 4 #实例属性
self.y = 5 #实例属性
C.count = 6 #类属性
类属性:类里面方法外面定义的变量称为类属性。类属性所属于类对象并且多个实例对象之间共享同一个类属性,说白了就是类属性所有的通过该类实例化的对象都能共享。
【例子】
class A():
a = 0 # 类属性
def __init__(self, xx):
# 使用类属性可以通过 (类名.类属性)调用。
A.a = xx
实例属性:实例属性和具体的某个实例对象有关系,并且一个实例对象和另外一个实例对象是不共享属性的,说白了实例属性只能在自己的对象里面使用,其他的对象不能直接使用,因为self是谁调用,它的值就属于该对象。
【例子】
class 类名():
__init__(self):
self.name = xx #实例属性
类属性和实例属性区别
类属性:类外面,可以通过实例对象.类属性和类名.类属性进行调用。类里面,通过self.类属性和类名.类属性进行调用。
实例属性 :类外面,可以通过实例对象.实例属性调用。类里面,通过self.实例属性调用。
实例属性就相当于局部变量。出了这个类或者这个类的实例对象,就没有作用了。
类属性就相当于类里面的全局变量,可以和这个类的所有实例对象共享。
【例子】
# 创建类对象
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
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'
3、尝试执行以下代码,并解释错误原因:
class C:
def myFun():
print('Hello!')
c = C()
c.myFun()
运行结果:
Traceback (most recent call last):
File "C:/Users/Administrator/PycharmProjects/untitled/task04.py", line 1, in <module>
class C():
File "C:/Users/Administrator/PycharmProjects/untitled/task04.py", line 4, in C
c = C()
NameError: name 'C' is not defined
4、按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。
要求:
平日票价100元
周末票价为平日的120%
儿童票半价
class Ticket():
def __init__(self, date, adult, child):
if date == 'work':
self.price = 100
else:
self.price = 120
self.child= child #儿童个数
self.adult = adult #成人个数
self.fees = 0
def __childfee(self):
self.fees += int(self.price * self.child * 0.5)#儿童票的总费用,其中儿童票半价
def __adultfee(self):
self.fees += int(self.price * self.adult)#成人票的总费用
def calculatefee(self):
self.__childfee()
self.__adultfee()
print("共花%d元"%self.fees)
z = Ticket('work',2,1) #工作日情况下2个成人1个孩子总票价
z.calculatefee()
运行结果:共花250元
5.上面提到了许多魔法方法,如__new__,init, str,rstr,getitem,__setitem__等等,请总结它们各自的使用方法。
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
new(cls[, …]) 在一个对象实例化的时候所调用的第一个方法,在调用__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__
#
# into A __new__
#
# 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__
#
# into A __new__
#
若__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
del(self) 析构器,当一个对象将要被系统回收之时调用的方法。
Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,当程序中有一个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 1;当程序中有两个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 2,依此类推,如果一个对象的引用计数变成了 0,则说明程序中不再有变量引用该对象,表明程序不再需要该对象,因此 Python 就会回收该对象。
大部分时候,Python 的 ARC 都能准确、高效地回收系统中的每个对象。但如果系统中出现循环引用的情况,比如对象 a 持有一个实例变量引用对象 b,而对象 b 又持有一个实例变量引用对象 a,此时两个对象的引用计数都是 1,而实际上程序已经不再有变量引用它们,系统应该回收它们,此时 Python 的垃圾回收器就可能没那么快,要等专门的循环垃圾回收器(Cyclic Garbage Collector)来检测并回收这种引用循环。
【例子】
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__
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) 的返回结果可读性强。也就是说,str 的意义是得到便于人们阅读的信息,就像下面的 ‘2019-10-11’ 一样。
repr(self) 的返回结果应更准确。怎么说,repr 存在的目的在于调试,便于开发者使用。
【例子】
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)
6.利用python做一个简单的定时器类
要求:
定制一个计时器的类。
start和stop方法代表启动计时和停止计时。
假设计时器对象t1,print(t1)和直接调用t1均显示结果。
当计时器未启动或已经停止计时时,调用stop方法会给予温馨的提示。
两个计时器对象可以进行相加:t1+t2。
只能使用提供的有限资源完成。
import time
class timer():
def __init__(self):
self.__initt = '计时尚未开始'
self.__begin = None
self.__end = None
self.__time = 0
def __str__(self):
return self.__initt
def __repr__(self):
return self.__initt
def start(self):
print("开始计时")
self.__begin = time.localtime()
def stop(self):
if not self.__begin:
print("请先调用start()开始计时")
return
self.__end = time.localtime()
self.__time = time.mktime(self.__end) - time.mktime(self.__begin)
self.__initt = '运行了%f秒' % self.__time
print("结束计时")
return self.__time
def __add__(self, other):
return 't1+t2共运行了%f秒' % (self.__time + other.__time)
t1 = timer()
print(t1)
t1.stop()
t1.start()
time.sleep(1)
t1.stop()
print(t1)
t2 = timer()
t2.start()
time.sleep(2)
t2.stop()
print(t2)
print(t1 + t2)
运行结果:计时尚未开始
请先调用start()开始计时
开始计时
结束计时
运行了1.000000秒
开始计时
结束计时
运行了2.000000秒
t1+t2共运行了3.000000秒