参数—可变参数—关键字参数—命名关键字参数
以下三个函数,分别设置函数的参数为*args可变参数,**关键字参数,命名关键字参数
def my_sum(*args): total = 0 for i in args: total += i return total def say_hello(**kwargs): print(kwargs) if 'name' in kwargs: print('你好%s' % kwargs['name']) elif 'age' in kwargs: if kwargs['age'] > 18: print('你是成年人') else: print('未成年') else: print('无信息') #*写在中间(命名关键字参数),前面的可以不用加关键字但后面的要加 def foo(a, b, c, *, name, age): print(a + b + c) print(name, ':', age) def main(): say_hello(name='王大锤', age=18) parm = {'name':'王大锤', 'age':16} #这里要加**否则出错 #不加*,显示TypeError: say_hello() takes 0 positional arguments but 1 was given #加*,显示TypeError: say_hello() takes 0 positional arguments but 2 were given say_hello(**parm) mylist = [1, 2, 3, 4] print(my_sum(*mylist)) #命名关键字参数,*在name之前,因此name开始要给关键字参数 foo(1, 2, c=3, name='王大锤', age='56') if __name__ == '__main__': main()
装饰器@property
我们常在受保护的对象属性前面加下划线来隐喻这个对象属性是不建议外界直接访问的,如果我们想要访问,则通过属性的getter(访问器)和setter(修改器)来操作,这时候需要使用@property包装器来包装getter和setter方法,使得对属性的访问既方便有安全。
class Teacher(object): def __init__(self, name, age, title): self._name = name self._age = age self._title = title @property def name(self): return self._name @property def age(self): return self._age @age.setter def age(self, age): self._age = age def teach(self, project): return '%s担任%s课老师' % (self._name, project) def watch_av(self): if self._age <= 18 : return '%s收看游戏直播' % (self._name) else: return '%s收看网红直播' % (self._name) def main(): a = Teacher('王大锤', 35, '高级') print(a.teach('语文')) print(a.watch_av()) a.age = 16 print(a.watch_av()) #a.name = '小明' #print(a.watch_av()) if __name__ == '__main__': main()
小结:age属性添加了setter修改器,因此可以重新赋值调用方法,但name属性没有使用setter,因此不能修改。
__slots__魔法
Python是门动态语言,我们可以再程序运行时给对象重新绑定新的属性或者方法,也可以对已经绑定的属性和方法进行解绑。如果我们要限定自定义类的属性,则需要使用__slots__变量来进行限定,但限定只对当前类的属性有效,对子类无效。
封装、继承和多态
封装:在程序设计中,封装(encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其实就是类或函数中属性的私有化。
继承:创建新类,可以从另一个已有类上将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的为父类(超类或基类);得到继承信息的为子类(派生类或衍生类),子类除了得到父类的属性和方法还可以定义属于自己的属性和方法。实际开发过程中我们还会用子类对象去替换父类对象,对应“里氏替换原则”。
多态:子类继承了父类的方法后,也可以将其修改为新方法,这称为方法的重写(override)。通过方法重写我们可以让父类的同一行为在不同子类中实现不同的版本,当我们调用这个经过子类重写的方法时,不同子类就会表现不同的行为,这就是多态(poly—morphism)。下面的程序就体现了这些概念。
class Person(object):
#魔法方法限定父类的属性 __slots__ = ('_name', '_age') def __init__(self, name, age): self._name = name self._age = age @property def name(self): return self._name @property #访问器 def age(self): return self._age @age.setter # 属性修改器 def age(self, age): if 15 <= age <= 25: self._age = age else: raise ValueError('无效的年龄') def watch_av(self): if self.age > 18: return '%s正在看片' % self._name else: return '%s只能看动画片' % self._name class Student(Person):#继承了Person的属性和方法 def __init__(self, name, age, grade): super().__init__(name, age) self._grade = grade def study(self,course): return '%s正在学习%s' % (self._name, course) #方法重写(override) #子类在继承父类方法之后 对方法进行了重新实现 #当我们给子类对象发送watch_av消息时执行的是子类重写过得方法 def watch_av(self): return '%s正在看片' %(self._name) class Teacher(Person): def __init__(self, name, age, title): super().__init__(name, age) self._title = title def teach(self, project): return '%s担任%s课老师' % (self._name, project) def main(): stu1 = Student('王大锤',35,98) print(stu1.study('语文')) print(stu1.watch_av()) tea = Teacher('王二锤', 45, '高级') print(tea.watch_av()) print(tea.teach('数学')) if __name__ == '__main__': main()
类之间的关系
类和类之间的关系有三种:is-a、has-a和use-a关系。
is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。
has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。
use-a关系通常称之为依赖,比如司机有一个驾驶去旅行,旅行使用到了汽车,那么司机和汽车的关系就是依赖关系。
说明:图片转自http://blog.csdn.net/jackfrued/article/details/79545466
静态方法和类方法
例如我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长未必能构造出三角形对象,因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这个方法很显然就不是对象方法,因为在调用这个方法时三角形对象尚未创建出来(因为都不知道三条边能不能构成三角形),所以这个方法是属于三角形类而并不属于三角形对象的。我们可以使用静态方法来解决这类问题,代码如下所示。
from math import sqrt class Triangle(object): def __init__(self, a, b, c): self._a = a self._b = b self._c = c @staticmethod def is_valid(a, b, c): return a + b > c and b + c > a and a + c > b def perimeter(self): return self._a + self._b + self._c def area(self): half = self.perimeter() / 2 return sqrt(half * (half - self._a) * (half - self._b) * (half - self._c)) def main(): a, b, c = 3, 4, 5 # 静态方法和类方法都是通过给类发消息来调用的 if Triangle.is_valid(a, b, c): t = Triangle(a, b, c) print(t.perimeter()) # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数 # print(Triangle.perimeter(t)) print(t.area()) # print(Triangle.area(t)) else: print('无法构成三角形.') if __name__ == '__main__': main()
和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。
from time import time, localtime, sleep class Clock(object): """数字时钟""" def __init__(self, hour=0, minute=0, second=0): self._hour = hour self._minute = minute self._second = second @classmethod def now(cls): ctime = localtime(time()) return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec) def run(self): """走字""" self._second += 1 if self._second == 60: self._second = 0 self._minute += 1 if self._minute == 60: self._minute = 0 self._hour += 1 if self._hour == 24: self._hour = 0 def show(self): """显示时间""" return '%02d:%02d:%02d' % \ (self._hour, self._minute, self._second) def main(): # 通过类方法创建对象并获取系统时间 clock = Clock.now() while True: print(clock.show()) sleep(1) clock.run() if __name__ == '__main__': main()
说明:上述代码出处: http://blog.csdn.net/jackfrued/article/details/79545466
python中类的抽象方法
Python从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过abc
模块的ABCMeta
元类和abstractmethod
包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。
from abc import ABCMeta, abstractmethod class Pet(object, metaclass=ABCMeta): """宠物""" def __init__(self, nickname): self._nickname = nickname @abstractmethod def make_voice(self): """发出声音""" pass class Dog(Pet): """狗""" def make_voice(self): print('%s: 汪汪汪...' % self._nickname) class Cat(Pet): """猫""" def make_voice(self): print('%s: 喵...喵...' % self._nickname) def main(): pets = [Dog('旺财'), Cat('凯蒂'), Dog('大黄')] for pet in pets: pet.make_voice() if __name__ == '__main__': main()
小结:上面的代码中,Dog
和Cat
两个子类分别对Pet
类中的make_voice
抽象方法进行了重写并给出了不同的实现版本,当我们在main
函数中调用该方法时,这个方法就表现出了多态行为(同样的方法做了不同的事情)。
这周的综合题很难,还要多敲代码啊,学习从模仿开始吧,相信熟能生巧!
综合题1:奥特曼打小怪兽
from abc import ABCMeta, abstractmethod from random import randint, randrange class Fighter(object, metaclass=ABCMeta): """战斗者""" # 通过__slots__魔法限定对象可以绑定的成员变量 __slots__ = ('_name', '_hp') def __init__(self, name, hp): """ 初始化方法 :param name: 名字 :param hp: 生命值 """ self._name = name self._hp = hp @property def name(self): return self._name @property def hp(self): return self._hp @hp.setter def hp(self, hp): self._hp = hp if hp >= 0 else 0 @property def alive(self): return self._hp > 0 @abstractmethod def attack(self, other): """ 攻击 :param other: 被攻击的对象 """ pass class Ultraman(Fighter): """奥特曼""" __slots__ = ('_name', '_hp', '_mp') def __init__(self, name, hp, mp): """ 初始化方法 :param name: 名字 :param hp: 生命值 :param mp: 魔法值 """ super().__init__(name, hp) self._mp = mp def attack(self, other): other.hp -= randint(15, 25) def huge_attack(self, other): """ 究极必杀技(打掉对方至少50点或四分之三的血) :param other: 被攻击的对象 :return: 使用成功返回True否则返回False """ if self._mp >= 50: self._mp -= 50 injury = other.hp * 3 // 4 injury = injury if injury >= 50 else 50 other.hp -= injury return True else: self.attack(other) return False def magic_attack(self, others): """ 魔法攻击 :param others: 被攻击的群体 :return: 使用魔法成功返回True否则返回False """ if self._mp >= 20: self._mp -= 20 for temp in others: if temp.alive: temp.hp -= randint(10, 15) return True else: return False def resume(self): """恢复魔法值""" incr_point = randint(1, 10) self._mp += incr_point return incr_point def __str__(self): return '~~~%s奥特曼~~~\n' % self._name + \ '生命值: %d\n' % self._hp + \ '魔法值: %d\n' % self._mp class Monster(Fighter): """小怪兽""" __slots__ = ('_name', '_hp') def attack(self, other): other.hp -= randint(10, 20) def __str__(self): return '~~~%s小怪兽~~~\n' % self._name + \ '生命值: %d\n' % self._hp def is_any_alive(monsters): """判断有没有小怪兽是活着的""" for monster in monsters: if monster.alive > 0: return True return False def select_alive_one(monsters): """选中一只活着的小怪兽""" monsters_len = len(monsters) while True: index = randrange(monsters_len) monster = monsters[index] if monster.alive > 0: return monster def display_info(ultraman, monsters): """显示奥特曼和小怪兽的信息""" print(ultraman) for monster in monsters: print(monster, end='') def main(): u = Ultraman('骆昊', 1000, 120) m1 = Monster('舒小玲', 250) m2 = Monster('白元芳', 500) m3 = Monster('王大锤', 750) ms = [m1, m2, m3] fight_round = 1 while u.alive and is_any_alive(ms): print('========第%02d回合========' % fight_round) m = select_alive_one(ms) # 选中一只小怪兽 skill = randint(1, 10) # 通过随机数选择使用哪种技能 if skill <= 6: # 60%的概率使用普通攻击 print('%s使用普通攻击打了%s.' % (u.name, m.name)) u.attack(m) print('%s的魔法值恢复了%d点.' % (u.name, u.resume())) elif skill <= 9: # 30%的概率使用魔法攻击(可能因魔法值不足而失败) if u.magic_attack(ms): print('%s使用了魔法攻击.' % u.name) else: print('%s使用魔法失败.' % u.name) else: # 10%的概率使用究极必杀技(如果魔法值不足则使用普通攻击) if u.huge_attack(m): print('%s使用究极必杀技虐了%s.' % (u.name, m.name)) else: print('%s使用普通攻击打了%s.' % (u.name, m.name)) print('%s的魔法值恢复了%d点.' % (u.name, u.resume())) if m.alive > 0: # 如果选中的小怪兽没有死就回击奥特曼 print('%s回击了%s.' % (m.name, u.name)) m.attack(u) display_info(u, ms) # 每个回合结束后显示奥特曼和小怪兽的信息 fight_round += 1 print('\n========战斗结束!========\n') if u.alive > 0: print('%s奥特曼胜利!' % u.name) else: print('小怪兽胜利!') if __name__ == '__main__': main()
综合题2:扑克游戏
from random import randrange class Card(object): """一张牌""" def __init__(self, suite, face): self._suite = suite self._face = face @property def face(self): return self._face @property def suite(self): return self._suite def __str__(self): all_suites = ('♠', '♥', '♣', '♦') if self._face == 1: face_str = 'A' elif self._face == 11: face_str = 'J' elif self._face == 12: face_str = 'Q' elif self._face == 13: face_str = 'K' else: face_str = str(self._face) return '%s%s' % (all_suites[self._suite], face_str) class Poker(object): """一副牌""" def __init__(self): self._cards = [] self._current = 0 for suite in range(4): for face in range(1, 14): card = Card(suite, face) self._cards.append(card) @property def cards(self): return self._cards def shuffle(self): """洗牌(随机乱序)""" self._current = 0 cards_len = len(self._cards) for index in range(cards_len): pos = randrange(cards_len) self._cards[index], self._cards[pos] = \ self._cards[pos], self._cards[index] @property def next(self): """发牌""" card = self._cards[self._current] self._current += 1 return card @property def has_next(self): """还有没有牌""" return self._current < len(self._cards) class Player(object): """玩家""" def __init__(self, name): self._name = name self._cards_on_hand = [] @property def name(self): return self._name @property def cards_on_hand(self): return self._cards_on_hand def get(self, card): """摸牌""" self._cards_on_hand.append(card) def arrange(self, card_key): """玩家整理手上的牌""" self._cards_on_hand.sort(key=card_key) # 排序规则-先根据花色再根据点数排序 def get_key(card): return (card.suite, card.face) def main(): p = Poker() p.shuffle() players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')] for _ in range(13): for player in players: player.get(p.next) for player in players: print(player.name + ':', end=' ') player.arrange(get_key) for card in player.cards_on_hand: print(card, end=' ') print() if __name__ == '__main__': main()
综合题3:分数计算
from math import gcd class Fraction(object): def __init__(self, num, den): if den == 0: raise ValueError('分母不能为0') self._num = num self._den = den #self.normalize() #初始化函数 #self.simplify() def simplify(self): if self._num != 0 and self._den != 1: factor = gcd(abs(self._num), abs(self._den)) if factor > 1: self._num //= factor self._den //= factor return self @property def num(self): return self._num @property def den(self): return self._den def add(self, other): return Fraction(self._num * other.den + self._den * other.num, self._den * other.den).simplify().normalize() def sub(self, other): return Fraction(self._num * other.den - self._den * other.num, self._den * other.den).simplify().normalize() def mul(self, other): return Fraction(self._num * other.num, self._den * other.den)\ .normalize().simplify() def div(self, other): return Fraction(self._num * other.den, self._den * other.num)\ .normalize().simplify() def __add__(self, other):#运算符重载,可以使用‘+’(重写和重载还是有点区别) return self.add(other) def __sub__(self, other): return self.sub(other) def __mul__(self, other): return self.mul(other) def __truediv__(self, other): return self.div(other) def normalize(self): if self._den < 0: self._num = -self._num self._den = -self._den return self def __str__(self): if self._num == 0: return '0' elif self._den == 1: return str(self._num) else: return '%d/%d' % (self._num, self._den) def main(): f1 = Fraction(-3, 6) f2 = Fraction(5, 1) print(f1) print(f2) print(f1.add(f2)) print(f1 + f2) print(f1 * f2) print(f1 - f2) print(f1 / f2) print((f1 - f2) * f2) if __name__ == '__main__': main()