说明:python中的组合与继承
组合:一个类的对象具备某种属性,该属性的值指向类外一个类的对象。
继承:描述类之间的从属关系。子类会用到父类中的一些特性。
class Course: #组合 def __init__(self, name, period, price): self.name = name self.period = period self.price = price def tell_info(self): msg = """ 课程名:%s 课程周期:%s 课程价钱:%s """ % (self.name, self.period, self.price) print(msg) class OldboyPeople: #父类 school = 'oldboy' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class OldboyStudent(OldboyPeople): 子类 def __init__(self, name, age, sex, stu_id): OldboyPeople.__init__(self, name, age, sex) self.stu_id = stu_id def choose_course(self): print('%s is choosing course' % self.name) class OldboyTeacher(OldboyPeople): #子类 def __init__(self, name, age, sex, level): OldboyPeople.__init__(self, name, age, sex) self.level = level def score(self, stu, num): stu.score = num print('老师[%s]为学生[%s]打分[%s]' % (self.name, stu.name, num)) # 创造课程 python = Course('python全栈开发', '5mons', 3000) linux = Course('linux运维', '5mons', 800) # python.tell_info() # linux.tell_info() # 创造学生与老师 stu1 = OldboyStudent('猪哥', 19, 'male', 1) tea1 = OldboyTeacher('egon', 18, 'male', 10) # 将学生、老师与课程对象关联/组合 stu1.course = python #把课程python对象内存地址添加到stu1对象名称空间中,之后可以直接调用到course中的函数 print(stu1.__dict__) tea1.course = linux stu1.course.tell_info() 直接用绑定方法调用组合里面的函数属性 tea1.course.tell_info()
组合:这里创建的course类中的对象python、linux分别是stu1和tea1的一个属性。
继承:这里的oldboyStudent和oldboyTeacher都是oldboyPeople的子类。它们继承了oldbotPeople类中的name、age和sex属性。
第一对象:在计算机科学中指可以在执行期创造并作为参数传递给其他函数或存入一个变量的实体。具有以下特性:
可以被存入变量或其他结构(list/dict/set...)
可以被作为参数传递给其他函数
可以被作为函数的返回值
可以在执行期创造,而无需完全在设计期全部写出
即使没有被系结至某一名称,也可以存在
这部分的细节内容可以参考博文:https://foofish.net/function-is-first-class-object.html
def print_directory_contents(sPath): """ 这个函数接受文件夹的名称作为输入参数, 返回该文件夹中文件的路径, 以及其包含文件夹中文件的路径。 """ # 补充代码
答案:
def print_directionary_contents(sPath):
import os
for sChild in os.llistdir(sPath):
sChildPath=os.path.join(sPath, sChild)
if os.path.isdir(sChildPath):
print_directory_contents(sChildPath)
else:
print sChildPath
注意:
- 命名规范要统一。如果样本代码中能够看出命名规范,遵循其已有的规范。
- 递归函数需要递归并终止。确保你明白其中的原理,否则你将面临无休无止的调用栈(callstack)。
- 我们使用
os
模块与操作系统进行交互,同时做到交互方式是可以跨平台的。你可以把代码写成sChildPath = sPath + '/' + sChild
,但是这个在Windows系统上会出错。- 熟悉基础模块是非常有价值的,但是别想破脑袋都背下来,记住Google是你工作中的良师益友。
- 如果你不明白代码的预期功能,就大胆提问。
- 坚持KISS原则!保持简单,不过脑子就能懂!
A0 = dict(zip(('a','b','c','d','e'),(1,2,3,4,5))) ----> A0={'a':1,'b':2,'c':3,'d':4,'e':5} A1 = range(10) ----> A1=[0,1,2,3,4,5,6,7,8,9] #range(x)表示从0到x-1的整数 A2 = [i for i in A1 if i in A0] ----> A2=[] A3 = [A0[s] for s in A0] ----> A3=[1,2,3,4,5] # A4 = [i for i in A1 if i in A3] ----> A4=[1,2,3,4,5] #同时在A1和A3中的数据 A5 = {i:i*i for i in A1} ----> A5=[0,1,4,9,16,25,36,49,64,81] A6 = [[i,i*i] for i in A1] ----> A6=[[0,0],[1,1],[2,4],[3,9],[4,16],[5,25],[6,36],[7,49],[8,64],[9,81]
Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意。Python中有一个被称为Global Interpreter Lock(GIL)的东西,它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度,使用threading包并不是一个很好的方法。
不过还是有很多理由促使我们使用threading包的。如果你想同时执行一些任务,而且不考虑效率问题,那么使用这个包是完全没问题的,而且也很方便。但是大部分情况下,并不是这么一回事,你会希望把多线程的部分外包给操作系统完成(通过开启多个进程),或者是某些调用你的Python代码的外部程序(例如Spark或Hadoop),又或者是你的Python代码调用的其他代码(例如,你可以在Python中调用C函数,用于处理开销较大的多线程工作)。
def f(x,l=[]):
for i in range(x):
l.append(i*i)
print l
f(2)
f(3,[3,2,1])
f(3)
答案:
[0,1]
[3,2,1,0,1,4]
[0,1,0,1,4]
分析:f(2)运行结束后,l变为[0,1]; 在运行f(3,[3,2,1])时,l指向的是在新的内存空间创建的新的列表[3,2,1],并在其后append 0,1,4。在运行第三个函数时,l有继续指向第一次运行时在内存空间中创建的列表,此时该列表为[0,1],所以,第三个函数运行结束后,l为[0,1,0,1,4]
“猴子补丁”指的是属性在运行时的动态替换。(在函数或对象定义好之后再去修改它们的行为)
这部分的内容有待加强了解(参考:https://blog.csdn.net/luo3300612/article/details/83986984)
*args
,**kwargs
?我们为什么要使用它们?函数的参数可以分为:位置参数,默认参数、可变参数、关键字参数和命名关键字参数。
位置参数:
def power(x, n):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
这里的x,n为位置参数,在调用函数时,传入的两个值按照位置一次赋值给参数x,n。
默认参数:
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
与上一个例子相比,我们将n设置为默认参数,当我们调用函数时仅传入一个参数时,则将参数值传给x,而n=2。所以这里power(5,2) <==> power(5)。在定义函数时,要把必要参数写在前面,默认参数写在后面。同时,定义默认参数一定要指向不变对象。
可变参数:
可变参数在调用函数时,传入的参数个数是可变的。可变参数的表示形式为在变量名前加*,(*args即为可变参数)。
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
calc(1,2) #sum=5
number=[1,2,3]
calc(*number) #*number把number中的所有元素编程可变参数传入,sum=14
关键字参数:
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。输入形式为**kwargs
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
person('Bob',35,city='Beijing') # print: name: Bob age: 35 other: {'city': 'Beijing'
person('Bob',35) #print: name: Bob age: 35 other: {}
命名关键字参数:
命名关键字参数需要一个特殊分隔符*
,*
后面的参数被视为命名关键字参数。命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错。
def person(name, age, *, city, job):
print(name, age, city, job)
这里的city和job均为命名关键字参数。
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了:
def person(name, age, *args, city, job):
print(name, age, args, city, job)
在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
@classmethod
, @staticmethod
, @property
?这些都是装饰器(decorator)。装饰器是一种特殊的函数,要么接受函数作为输入参数,并返回一个函数,要么接受一个类作为输入参数,并返回一个类。@标记是语法糖(syntactic sugar),可以让你以简单易读得方式装饰目标对象。
阅读下面的代码,它的输出结果是什么?
class A(object):
def go(self):
print "go A go!"
def stop(self):
print "stop A stop!"
def pause(self):
raise Exception("Not Implemented")
class B(A):
def go(self):
super(B, self).go()
print "go B go!"
class C(A):
def go(self):
super(C, self).go()
print "go C go!"
def stop(self):
super(C, self).stop()
print "stop C stop!"
class D(B,C):
def go(self):
super(D, self).go()
print "go D go!"
def stop(self):
super(D, self).stop()
print "stop D stop!"
def pause(self):
print "wait D wait!"
class E(B,C): pass
a = A()
b = B()
c = C()
d = D()
e = E()
a.go()
b.go()
c.go()
d.go()
e.go()
a.stop()
b.stop()
c.stop()
d.stop()
e.stop()
a.pause()
b.pause()
c.pause()
d.pause()
e.pause()
结果:
a.go() # go A go! b.go() # go A go! # go B go! c.go() # go A go! # go C go! d.go() # go A go! # go C go! # go B go! # go D go! e.go() # go A go! # go C go! # go B go! a.stop() # stop A stop! b.stop() # stop A stop! c.stop() # stop A stop! # stop C stop! d.stop() # stop A stop! # stop C stop! # stop D stop! e.stop() # stop A stop! a.pause() # ... Exception: Not Implemented b.pause() # ... Exception: Not Implemented c.pause() # ... Exception: Not Implemented d.pause() # wait D wait! e.pause() # ...Exception: Not Implemented
class Node(object):
def __init__(self,sName):
self._lChildren = []
self.sName = sName
def __repr__(self):
return "".format(self.sName)
def append(self,*args,**kwargs):
self._lChildren.append(*args,**kwargs)
def print_all_1(self):
print self
for oChild in self._lChildren:
oChild.print_all_1()
def print_all_2(self):
def gen(o):
lAll = [o,]
while lAll:
oNext = lAll.pop(0)
lAll.extend(oNext._lChildren)
yield oNext #yield类似于迭代器,在运行过程中输出其后面的值
for oNode in gen(self):
print oNode
oRoot = Node("root")
oChild1 = Node("child1")
oChild2 = Node("child2")
oChild3 = Node("child3")
oChild4 = Node("child4")
oChild5 = Node("child5")
oChild6 = Node("child6")
oChild7 = Node("child7")
oChild8 = Node("child8")
oChild9 = Node("child9")
oChild10 = Node("child10")
oRoot.append(oChild1)
oRoot.append(oChild2)
oRoot.append(oChild3)
oChild1.append(oChild4)
oChild1.append(oChild5)
oChild2.append(oChild6)
oChild4.append(oChild7)
oChild3.append(oChild8)
oChild3.append(oChild9)
oChild6.append(oChild10)
# 说明下面代码的输出结果
oRoot.print_all_1()
oRoot.print_all_2()
运行结果:
oRoot.print_all_1()
会打印下面的结果:
oRoot.print_all_1()
会打印下面的结果:
引用循环
(reference cycle)。垃圾回收器会定时寻找这个循环,并将其回收。举个例子,假设有两个对象o1
和o2
,而且符合o1.x == o2
和o2.x == o1
这两个条件。如果o1
和o2
没有其他代码引用,那么它们就不应该继续存在。但它们的引用计数都是1。[random.random() for i in range(100000)]
。你如何证明自己的答案是正确的。def f1(lIn):
l1 = sorted(lIn)
l2 = [i for i in l1 if i<0.5]
return [i*i for i in l2]
def f2(lIn):
l1 = [i for i in lIn if i<0.5]
l2 = sorted(l1)
return [i*i for i in l2]
def f3(lIn):
l1 = [i*i for i in lIn]
l2 = sorted(l1)
return [i for i in l1 if i<(0.5*0.5)]
答案:
按执行效率从高到低排列:f2、f1和f3。要证明这个答案是对的,你应该知道如何分析自己代码的性能。Python中有一个很好的程序分析包,可以满足这个需求。
import cProfile lIn = [random.random() for i in range(100000)] cProfile.run('f1(lIn)') cProfile.run('f2(lIn)') cProfile.run('f3(lIn)')
为了向大家进行完整地说明,下面我们给出上述分析代码的输出结果:
>>> cProfile.run('f1(lIn)') 4 function calls in 0.045 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.009 0.009 0.044 0.044:1(f1) 1 0.001 0.001 0.045 0.045 :1( ) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.035 0.035 0.035 0.035 {sorted} >>> cProfile.run('f2(lIn)') 4 function calls in 0.024 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.008 0.008 0.023 0.023 :1(f2) 1 0.001 0.001 0.024 0.024 :1( ) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.016 0.016 0.016 0.016 {sorted} >>> cProfile.run('f3(lIn)') 4 function calls in 0.055 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.016 0.016 0.054 0.054 :1(f3) 1 0.001 0.001 0.055 0.055 :1( ) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.038 0.038 0.038 0.038 {sorted}
参考:
https://www.cnblogs.com/wangchaowei/p/8184721.html 《Python笔试、面试 【必看】》
https://blog.csdn.net/XiaoMaGe1996/article/details/80828864 《python之继承中组合用法与菱形继承关系查找法》
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000 《廖雪峰的官方网站》