1.递归函数
- 自己调用自己
- 一般递归100多次,没有解决问题就不要用了
1.1用递归解决年龄问题
def age(n):
if n == 1: #终止条件
return 23
else:
return age(n-1)+2 #通相式
print(age(4))
1.2二分查找
前提条件:列表必须是有序的不重复数字列表
l = [1,2,3,4,5]
def search(l, target,start=0,end=len(l)-1):
if end >= start:
mid_index = end-start // 2 + start
if target > l[mid_index]:
return search(l,target,start=mid_index+1,end=len(l)-1)
elif target < l[mid_index]:
return search(l,target,start=0,end=mid_index-1)
elif target == l[mid_index]:
return mid_index
else:
return 'no this value'
else:
return 'no this value'
print(search(l,3))
2.面向对象
实际工作中,python都是面向对象,写代码,或者面向对象+函数。
2.1面向对象编程:上帝式思维
class Person: #class关键字,定义了一个类
'''
类里面所有内容
'''
animal = '高级动物' #静态变量
soup = '有思想'
def __init__(self,name,eye):
self.name = name #属性
self.eye = eye
print(666)
def work(self): #动态变量,方法
print('工作。。。。')
2.2什么是类?
- “类”是具有相同属性和技能的一类事物
- “对象”实例化的一个类,是类的具体体现
- 类如何调用查看静态变量,动态变量\
- 特殊的类属性
类名.name# 类的名字(字符串)
类名.doc# 类的文档字符串
类名.base# 类的第一个父类(在讲继承时会讲)
类名.bases# 类所有父类构成的元组(在讲继承时会讲)
类名.dict# 类的字典属性
类名.module# 类定义所在的模块
类名.class# 实例对应的类(仅新式类中)
2.2.1类操作静态变量有两种方式:
1.类名.dict方法
print(Person.__dict__) #打印所有静态变量,只能查看,不能增加,修改,删除
print(Person.__dict__['animal'])
2.类名.变量名
print(Person.animal)
Person.animal = '低等动物' #可以增,删,改,查
2.2.2类操作方法有两种方式:
1类名.dict[方法名]()
print(Person.__dict__['work']())
3.2类名.方法名
Person.work(11)
2.3对象
类名() #实例化一个对象
p1 = Persion() #p1对象,实例化对象,类名()过程就叫做实例化。
p2 = Person()只要创建一个类,里面的内容就已经加载到内存。
p1 = Person() #init叫构造方法,创建对象时候自动调用。
内部进行三步:
1.实例化一个对象,在内存中产生一个对象空间。>
2.自动执行init方法,并将这个空间对象传给self>
3.通过构造方法里的代码给空间添加一些属性,并返回给对象。
2.4对象操作属性:
1.print(p1.dict)
2.对象.变量名
print(p1.name)
3.增删改查
p1.color = red
print(p1.color)
2.5对象操作方法有2种:
p1 = Person(‘Morgan’,’big’)
1. 对象.方法名()
p1.work()
print(p1)
2.类名.方法名(对象)
Person.work(111)
3.对象可以访问类的静态变量
print(p1.animal)
通过实例化对象查找属性,先从对象空间找,没有则通过类对象指针从类空间找。
3.组合
就是让不同的类混合并加入到其他类中,来增强功能和代码重用性
class Game_person:
def __init__(self,nickname,sex,hp,ad):
self.nickname = nickname
self.sex = sex
self.hp = hp
self.ad = ad
def attack(self,p):
p.hp -= self.ad
print('%s攻击了%s,%s还剩%s血量'%(self.nickname,p.nickname,p.nickname,p.hp))
def weapon_attack(self,武器):
self.武器 = 武器 #斧子对象
class Weapon:
def __init__(self,name,ad):
self.name=name
self.ad=ad
def fight(self,p1,p2):
p2.hp -= self.ad
print('%s使用%s打了%s%s血,%s还剩%s滴血'\
%(p1.nickname,self.name,p2.nickname,self.ad,p2.nickname,p2.hp))
ts = Game_person('泰森','男',200,50)
barry = Game_person('太白','男',100,10)
fuzi = Weapon('斧子',60)
barry.weapon_attack(fuzi)
barry.武器.fight(barry,ts)
3.1面向对象三大特点
3.1.1封装
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
【好处】
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
【封装原则】
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问。
3.1.1.1私有变量和私有方法
私有变量
- 在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
- 类中所有双下划线开头的名称如_x都会自动变形成:类名__x的形式:
class A:
__N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
def __init__(self):
self.__X=10 #变形为self._A__X
def __foo(self): #私有函数,变形为_A__foo
print('from A')
def bar(self):
self.__foo() #只有在类内部才可以通过__foo的形式访问到.
#A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的_x不会覆盖在父类定义的__x,因为子类中变形成了:子类名_x,而父类中变形成了:父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
私有方法
- 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的,例如以下:
class A:
def fa(self):
print('from A')
def test(self):
self.fa()
class B(A):
def fa(self):
print('from B')
b = B()
b.test() #输出:from B
====================================
#把fa定义成私有的,即__fa
====================================
class A:
def __fa(self):
print('from A')
def test(self):
self.__fa()
class B(A):
def __fa(self):
print('from B')
b = B()
b.test() #输出:from A
3.1.2继承
- 可以有效节省代码。
- python3中,所有的类都默认继承object类,继承object类的类称为新式类。
- python中,类分为两种:新式类,经典类。
- 但是python3中只有新式类,python2中都有。
- 新式类继承顺序:遵循广度优先(是一个算法,每个节点只走一次,用最少的次数。)
- 经典类继承顺序:遵循深度优先
3.1.2.1继承:单继承,多继承。
重写父类方法
super().init
class FooParent:
def __init__(self):
self.parent = 'I \'m the parent.'
print('Parent')
def bar(self, message):
print("%s from parent" % message)
class FooChild(FooParent):
def __init__(self):
super(FooChild, self).__init__()
print('child')
def bar(self, message):
super(FooChild, self).bar(message)
print('child bar function')
print(self.parent)
if __name__ == '__main__':
fooChild = FooChild()
fooChild.bar('helloworld')
输出:
Parent
child
helloworld from parent
child bar function
I 'm the parent.
单继承:一个一个往上找。
多继承:先平级找,然后再下级中找。
Tips
类名.mro()查询继承顺序
经典类:深度优先
python2默认经典类,如果改成新式类就让类继承(object)
接口回去看
3.3多态
Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。
class Animal:
def run(self):
print('Animal is running...')
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
pass
class Tortoise(Animal): #你会发现,新增一个Animal的子类,不必对run_twice()做任何修改,实际上,
# 任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
def run(self):
print('Tortoise is running slowly...')
def run_twice(animal):
animal.run()
animal.run()
dog = Dog()
cat = Cat()
run_twice(Tortoise())
多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:
对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
对扩展开放:允许新增Animal子类;
对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
继承还可以一级一级地继承下来,就好比从爷爷到爸爸、再到儿子这样的关系。而任何类,最终都可以追溯到根类object,这些继承关系看上去就像一颗倒着的树。
3.4抽象类与接口类
继承有两种用途:
- 继承基类的方法,并且做出自己的改变或者扩展(代码重用)
- 声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
3.4.1接口继承
继承的第二种含义非常重要。它又叫“接口继承”。
借用abc模块来实现接口
class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self,money):
pass
class Wechatpay(Payment):
def fuqian(self,money):
print('微信支付了%s元'%money)
p = Wechatpay() #TypeError: Can't instantiate abstract class Wechatpay with abstract methods pay
=============================================================
from abc import ABCMeta, abstractmethod
class IStream(metaclass=ABCMeta):
@abstractmethod
def read(self, maxbytes=-1):
print('read1')
@abstractmethod
def write(self, data):
print('write1')
class SocketStream(IStream):
def read(self, maxbytes=-1):
return 'read'
def write(self, data):
return 'write'
p = SocketStream()
print(p.read()) #输出:read
上面例子详细说明:
@abstractmethod 还能注解静态方法、类方法和 properties 。 你只需保证这个注解紧靠在函数定义前即可:
class A(metaclass=ABCMeta):
@property
@abstractmethod
def name(self):
pass
@name.setter
@abstractmethod
def name(self, value):
pass
@classmethod
@abstractmethod
def method1(cls):
pass
@staticmethod
@abstractmethod
def method2():
pass
4.call内置函数的作用和用法
Python中,一个特殊的魔术方法可以让类的实例的行为表现的像函数一样,你可以调用他们,将一个函数当做一个参数传到另外一个函数中等等。这是一个非常强大的特性让Python编程更加舒适甜美。 call(self, [args…])
class X(object):
def __init__(self, a, b, range):
self.a = a
self.b = b
self.range = range
def __call__(self, a, b):
self.a = a
self.b = b
print('__call__ with ({}, {})'.format(self.a, self.b))
def __del__(self, a, b, range):
del self.a
del self.b
del self.range
>>> xInstance = X(1, 2, 3)
>>> xInstance(1,2)
__call__ with (1, 2)