17.2 Python入门之类属性访问

通常通过点操作符访问对象属性,Python也提供了魔法方法来重写,从而控制对象的属性访问。

1)_ _getattr_ _(self,name)

定义当用户试图获取不存在的属性时的行为


2)_ _getattribute_ _(self,name)

定义当类的属性被访问时的行为


3)_ _setattr_ _(self,name)

定义当属性被设置时的行为


4)_ _delattr_ _(self,name)

定义当属性被删除时的行为


class C:

    def _ _getattribute_ _(self,name):

      print('getattribute')

      return super().__getattribute_ _(name)


def _ _setattr_ _(self,name,value):

      print('setattr')

      return super().__setattr_ _(name,value)


def _ _delattr_ _(self,name):

      print('delattr')

      return super().__delattr_ _(name)


def _ _getattr_ _(self,name):

     print('getattr')


>>>c = C()

>>>c.x

getattribute

getattr

>>>c.x = 1

setattr

>>>c.x

getattribute

1

>>>del c.x

delattr

>>>setattr(c,'y','Yellow')

setattr


警惕死循环陷阱


class Rectangle:

  def _ _init_ _(self,width=0,height=0):

   self.width = width

   self.height = height

  def _ _setattr_ _(self,name,value):

    if name == 'square':

      self.width = value

      self.height = value

    else:

       self.name = value   #修改为 super()._ _setattr_ _(name,value)

  def getArea(self):

      return self.width * self.height


 若是未修改的程序,执行r1 = Rectangle(4,5)会陷入死循环,因为构造方法试图给属性赋值,自动触发_ _setattr_ _()方法,则执行else部分,再次对属性赋值,则又触发_ _setattr_ _()方法


修改部分通过调用基类的方法来实现赋值


>>>r1 = Rectangle(4,5)

>>>r1.getArea()

20

>>>r2.square = 10

>>>r2.getArea()

100


另一种方法是对特殊属性_ _dict_ _赋值,该属性是用于以字典形式显示出当前对象的所有属性以及相对应的值


>>>r1._ _dict_ _

{'height':10,'width':10}


则修改为

else:

self._ _dict_ _[name] = value


描述符


描述符就是将某特殊类型的类(至少在类里定义了_ _get_ _(),_ _set_ _(),

_ _delete_ _()三个特殊方法的任意一个)的实例指派给另一个类的属性


1._ _get_ _(self,instance,owner)

用于访问属性,返回属性的值


2._ _set_ _(self,instance,value)

不返回任何内容,在属性设置中调用


3._ _delete_ _(self,instance)

不返回任何内容,控制删除操作


class MyDescriptor:

    def  _ _get_ _(self,instance,owner):

       print('getting',self,instance,owner)

    def _ _set_ _(self,instance,value):

      print("setting",self,instance,value)

    def _ _delete_ _(self,instance):

      print("deleting",self,instance)


MyDescriptor全都实现了这三个方法,并将类的实例指派给Test类的实例,即其是描述符类


class Test:

  x = MyDescriptor()


>>>test = Test()

>>>test.x

getting

当访问x属性,Python自动调用特殊类型类的_ _get_ _()方法,参数分别是:self是描述符类自身的实例,instance是描述符拥有者所在类的实例,即test,owner是描述符拥有者所在类的本身(Test)


>>>test.x = 'man'

setting .....man

对x属性赋值时,Python自动调用_ _set_ _()方法,前两个参数同上,最后一个参数是赋的值(man)


>>>del test.x

deleting





定义属于自己的MyProperty


class MyProperty:

    def _ _init_ _(self,fget = None, fset = None,fdel = None):

      self.fget = fget

      self.fset = fset

      self.fdel = fdel

  

def  _ _get_ _(self,instance,owner):

      return self.fget(instance)

    def _ _set_ _(self,instance,value):

       self.fset(instance,value)

    def _ _delete_ _(self,instance):

      self.fdel(instance)


class C:

    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 = MyProperty(getX,setX,delX)  #方法为参数传入??


MyProperty为描述符类,x为C的属性


>>>c = C()

>>>c.x = 'man'

>>>c.x

'man'

>>>c._x

'man'

>>>del c.x

>>>c._x

#AttributeError


定制序列(容器)


Python中序列类型(列表,元组,字符串)和映射类型(字典)都是属于容器类型


定制容器的协议(指南)


1)希望容器不可变,需要定义_ _len_ _(),_ _getitem_ _()方法


2)希望容器可变,除了_ _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)

     #*args为收集参数,使用列表下标作为字典键,不用元素值(可能破坏键唯一性)

   def _ _len_ _(self):

      return len(self.values)

   def _ _getitem_ _(self,key):

     self.count[key] += 1   #键所对应的值加一

     return self.values[key]

  


>>>c1 = CountList(1,3,5,7,9)

>>>c2 = CountList(2,4,6,8,10)

>>>c1[1]

3

>>>c2[1]

4

>>>c1[1] + c2[1]

7

>>>c1.count

{0:0,1:2,2:0,3:0,4:0}  #记录访问次数

>>>c2.count

{0:0,1:2,2:0,3:0,4:0}


迭代器


每一次重复过程称为一次迭代,每一次迭代结果作为下一次迭代的初始值,提供迭代方法的容器称为迭代器,如序列(列表,元组,字符串),字典,都支持迭代操作


>>>for i in "Fish"

    print(i)

F

i

s

h


分析:"Fish"是字符串,为容器,也是迭代器,for语句触发迭代器的迭代功能,每次从容器拿出一个元素(迭代操作)


>>>links = {'dog':'gou',\

             'cat':'mao'}

for each in links:

    print('%s -> %s'%(each,links[each]))

dog -> gou

cat -> mao


关于迭代的BIF


对容器对象调用iter()方法,得到其迭代器,调用next()迭代器会返回下一个值,若迭代器没有值可以返回,则抛出StopIteration异常


>>>string = "Go"

>>>it = iter(string)

>>>next(it)

'G'

>>>next(it)

'o'

>>>next(it)

#StopIteration


所以for语句实际工作过程:


>>>string = "Go"

>>>it = iter(string)

>>>while True

    try:

     each = next(it)

    except StopIteration:

     break

    print(each)


G

o


可见for语句自动调用next()方法和处理异常


关于迭代器的魔法方法


一个容器若是迭代器,则必须实现_ _iter_ _()魔法方法,该方法返回迭代器本身,而

_ _next_ _()方法决定迭代规则,尤为重要!


>>>class Fibs:

     def _ _init_ _(self):

        self.a = 0

        self.b = 1

    def _ _iter_ _(self):

       return self

    def _ _next_ _(self):

       self.a,self.b = self.b,self.a + self.b

        return self.a

>>>fibs = Fibs()

>>>for each in fibs:

   if each < 20:

      print(each)

   else:

      break


1

1

2

3

5

8

13


但上述例子没有终止条件,修改为


>>>class Fibs:

     def _ _init_ _(self):

        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(10)

>>>for each in fibs:

    print(each)

1

1

2

3

5

8


生成器


不涉及魔法方法,仅通过普通的函数就可以实现


生成器实际是迭代器的一种实现,迭代器需要自己定义一个类和实现相关的方法,而生成器只需要在函数中添加yield语句


生成器的使用使得Python协同程序(生成器暂时挂起函数,函数暂停,保留函数的局部变量,之后从断点继续或重新开始)概念得以实现


普通的函数从第一行代码执行,结束于return语句,异常或者函数所有语句执行完毕,蛋函数交还控制权,则局部变量数据全部丢失


>>>def myGen():

     print("activity")

     yield 1

     yield 2


>>>myG =myGen()

>>>next(myG)

activity

1

>>>next(myG)

2

>>>next(myG)

#StopIteration


当函数结束,异常抛出,因为for语句自动调用next()方法和处理异常,同样可作用于生成器

>>>for i in myGen():

      print(i)

activity

1

2


上述的斐波那契用生成器实现

>>>def fibs():

    a = 0

    b = 1

  while True:

    a,b = b,a + b

    yield a


>>>for each in fibs():

    if each > 10:

      break

    print(each)

1

1

2

3

5

8


列表推导式


>>> a = [i for i in range(100) if not(i%2) and i%3]

   #100以内能被2整除但不能被3整除


字典推导式


>>>b = {i:i % 2 == 0 for i in range(1)}

>>>b

{0:True,1:False}


集合推导式


>>>c = {i for i in[1,1,2,3]}

>>>c

{1,2,3}


不存在字符串推导式,在双引号内所有东西都成为字符串


>>>d = "i for i in 'I love running'"

>>>d

"i for i in 'I love running'"


元组推导器


>>> e = (i for i in range(5))

>>>e

<generator object <genexpr> at Ox03135300>

说明其为生成器推导器


>>>next(e)

0

>>>next(e)

1

>>>next(e)

2

for语句打印剩下内容

>>>for each in e:

     print(each)

3

4


生成器作为函数参数,可直接写推导式,不用加小括号


>>>sum(i for i in range(100) if i % 2)

2500

 



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
allegro 17.2是一种用于开发2D游戏和应用程序的跨平台图形库。它提供了丰富的功能和工具,使开发者可以轻松地创建各种类型的游戏和应用程序。 对于初学者来说,学习和入门使用allegro 17.2的过程相对简单。以下是一些入门的步骤和建议: 1. 安装和配置:首先,你需要从allegro的官方网站下载和安装最新版本的allegro 17.2。安装过程相对简单,只需要按照指示进行操作即可。完成安装后,你还需要在编译器中配置allegro的路径,以便在开发中正确地引用库文件和头文件。 2. 学习文档和示例代码:allegro 17.2提供了详细的官方文档和示例代码,这些资源将是你入门学习的宝贵资料。你可以通过阅读文档来了解库的各种功能和用法,并通过示例代码来理解和实践这些概念。 3. 学习基础知识:在开始使用allegro 17.2之前,你需要掌握一些基础的编程知识,例如C++或其他支持allegro的编程语言。如果你是C++的初学者,那么学习C++语法和基本概念将对你的学习过程有帮助。 4. 编写简单的程序:一旦你掌握了allegro的基本用法,你可以尝试编写一些简单的程序来熟悉库的使用。例如,你可以创建一个简单的窗口,显示一些图形或文字,并尝试处理用户的输入。通过编写这些小型程序,你将逐渐熟悉allegro的不同概念和功能。 5. 探索更多功能:一旦你熟悉了allegro的基础知识,你可以深入探索库的更多功能和工具。例如,你可以学习如何处理声音和音效、处理碰撞检测、制作动画等等。不断探索和实践,将帮助你更好地理解和应用allegro。 总的来说,allegro 17.2是一个功能强大且易于学习的图形库,非常适合初学者入门。通过学习文档、实践示例代码和编写简单的程序,你将能够快速掌握allegro的基本概念和用法,并开始开发自己的游戏和应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值