Skip to content

Latest commit

 

History

History
441 lines (286 loc) · 15.2 KB

qa-oop.md

File metadata and controls

441 lines (286 loc) · 15.2 KB

Python 'self' 解释

问题 链接

self关键字的作用是什么? 我理解他用户在创建class时具体化实例,但我无法理解为何需要给每个方法加入self作为参数.

举例,在ruby中,我这么做:

class myClass
    def myFunc(name)
        @name = name
    end
end

我可以很好地理解,非常简单.但是在Python中,我需要去加入self:

class myClass:
    def myFunc(self, name):
        self.name = name

有谁能解释下么?

使用self关键字的原因是,Python没有@语法用于引用实例属性.Python决定用一种方式声明方法:实例对象自动传递给属于它的方法,但不是接收自动化:方法的第一个参数是调用这个方法的实例对象本身.这使得方法整个同函数一致,并且由你自己决定真实的名(虽然self是约定,但当你使用其他名的时候,通常人们并不乐意接受).self对于代码不是特殊的,只是另一个对象.

Python本来可以做一些用来区分真实的名字和属性的区别 —— 像Ruby有的特殊语法,或者像C++/Java的命令声明,或者其他可能的的语法 —— 但是Python没有这么做.Python致力于使事情变得明确简单,让事情是其本身,虽然并不是全部地方都这么做,但是实例属性石这么做的!这就是为什么给一个实例属性赋值时需要知道是给哪个实例赋值,并且,这就是为什么需要self

举例

class Vector(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def length(self):
        return math.sqrt(self.x ** 2 + self.y ** 2)

等价于

def length_global(vector):
    return math.sqrt(vector.x ** 2 + vector.y ** 2)

另外

v_instance.length()
转为
Vector.length(v_instance)

为什么Python的'private'方法并不是真正的私有方法

问题 链接

Python允许我们创建'private' 函数:变量以两个下划线开头,像这样: __myPrivateMethod(). 但是,如何解释:

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()
>>> obj.myPublicMethod()
public method
>>> obj.__myPrivateMethod()
Traceback (most recent call last):
File "", line 1, in
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
>>> obj._MyClass__myPrivateMethod()
this is private!!

dir(obj) 和 obj._MyClass__myPrivateMethod()

回答

‘private'只是用作,确保子类不会意外覆写父类的私有方法和属性.不是为了保护外部意外访问而设计的!

例如:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

当然,这对于两个同名的类没有作用

另外,可以查看diveintopython的解释 入口

Python中类方法的作用是什么

问题 链接

我现在意识到,我不需要像我在使用java的static方法那样使用类方法,但是我不确定什么时候使用

谁能通过一个好的例子解释下Python中的类方法,至少有人能告诉我什么时候确实需要使用类方法

类方法用在:当你需要使用不属于任何明确实例的方法,但同时必须涉及类.有趣的是,你可以在子类中覆写,这在Java的static方法和Python的模块级别函数中是不可能做到的

如果你有一个MyClass, 并且一个模块级别函数操作MyClass(工厂,依赖注入桩等等), 声明一个类方法.然后这个类方法可以在子类中调用

Python中 new 和 __init__的用法

问题 链接

我很疑惑,为何__init__总是在__new__之后调用

如下

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return super(A, cls).__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self
        print ""

a1 = A()
a2 = A()
a3 = A()

输出

NEW
INIT

EXISTS
INIT

EXISTS
INIT

有木有人可以解释一下

来自 链接

使用__new__,当你需要控制一个实例的生成

使用__init__,当你需要控制一个实例的初始化

__new__是实例创建的第一步.最先被调用,并且负责返回类的一个新实例.

相反的,__init__不返回任何东西,只是负责在实例创建后进行初始化

通常情况下,你不必重写__new__除非你写一个子类继承不可变类型,例如str,int,unicode或tuple

你必须了解到,你尝试去做的用Factory可以很好地解决,并且是最好的解决方式.使用__new__不是一个简洁的处理方式,一个factory例子

如何获取一个实例的类名

问题 链接

x.__class__.__name__

@staticmethod和@classmethod的区别

问题 链接

staticmethod,静态方法在调用时,对类及实例一无所知

仅仅是获取传递过来的参数,没有隐含的第一个参数,在Python里基本上用处不大,你完全可以用一个模块函数替换它

classmethod, 在调用时,将会获取到其所在的类,或者类实例,作为其第一个参数

当你想将函数作为一个类工厂时,这非常有用: 第一个参数是类,你可以实例化出对应实例对象,甚至子类对象。

可以观察下 dict.fromkey(),是一个类方法,当子类调用时,返回子类的实例

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
...
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>

如何定义静态方法(static method)

问题 链接

使用 staticmethod装饰器

class MyClass(object):
    @staticmethod
    def the_static_method(x):
        print x
MyClass.the_static_method(2) # outputs 2

Python中的类变量(环境变量)

问题 链接

在类中定义的变量,不在方法定义中,成为类变量或静态变量

>>> class MyClass:
...     i = 3
...
>>> MyClass.i
3

i是类级别的变量,但这里要和实例级别的变量i区分开

>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

这和C++/java完全不同,但和C#区别不大,C#不允许类实例获取静态变量

具体见 what the Python tutorial has to say on the subject of classes and class objects

另外,静态方法

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

如何判断一个对象是否拥有某个属性

问题 链接

if hasattr(a, 'property'):
    a.property

两种风格

EAFP(easier to ask for forgiveness than permission)

LBYL(look before you leap)

相关内容 EAFP vs LBYL (was Re: A little disappointed so far) EAFP vs. LBYL @Code Like a Pythonista: Idiomatic Python

try:
    doStuff(a.property)
except AttributeError:
    otherStuff()
or

if hasattr(a, 'property'):
    doStuff(a.property)
else:
    otherStuff()

Python中有没有简单优雅的方式定义单例类

问题 链接

我不认为有必要,一个拥有函数的模块(不是类)可以作为很好的单例使用,它的所有变量被绑定到这个模块,无论如何都不能被重复实例化

如果你确实想用一个类来实现,在python中不能创建私有类或私有构造函数,所以你不能隔离多个实例而仅仅通过自己的API来访问属性

我还是认为将函数放入模块,并将其作为一个单例来使用是最好的办法

理解Python的Super()和init方法

问题 链接

尝试着去理解super().从表面上看,两个子类都能正常创建.我只是好奇他们两者之间的不同点

class Base(object):
    def __init__(self):
        print "Base created"

class ChildA(Base):
    def __init__(self):
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        super(ChildB, self).__init__()

print ChildA(),ChildB()

回答

Super让你避免明确地引用基类,这是一点。最大的优势是,当出现多重继承的时候,各种有趣的情况就会出现。查看super官方文档.

另外注意,在Python3.0中,可以使用super().init() 代替 super(ChildB, self).init().IMO略有优势.

在Python中,如何判断一个对象iterable?

问题 链接

  1. 检查__iter__对序列类型有效,但是对例如string,无效

     try:
         iterator = iter(theElement)
     except TypeError:
         # not iterable
     else:
         # iterable
    
     # for obj in iterator:
     #     pass
    
  2. 使用collections

     import collections
    
     if isinstance(e, collections.Iterable):
         # e is iterable
    

构建一个基本的Python迭代器

问题链接

在Python中,迭代器对象遵循迭代器协议,这意味着它提供了两种方法: __iter__()next()__iter__()返回一个迭代器对象并且在循环开始时就隐式的被调用。next()方法返回下一个值,并在循环的每一次增量中被调用。当没有值需要返回时,next()引发一个StopIteration异常,这个异常被循环结构隐式的捕获从而停止迭代。

这有一个简单计数例子:

class Counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

    def next(self): # Python 3: def __next__(self)
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1

for c in Counter(3, 8):
    print c

上述会打印出:

3
4
5
6
7
8

这个用生成器写会更简单一些,下面是之前答案的翻写:

def counter(low, high):
    current = low
    while current <= high:
        yield current
        current += 1

for c in counter(3, 8):
    print c

打印出来的内容是一样的。在后台,生成器对象支持迭代器协议,大体上对Counter类做一些同样事情。

Iterators and Simple Generators,David Mertz的这篇文章,是一篇对迭代器非常好的介绍。

在Python中,抽象类和接口有什么区别?

问题链接

看看下面这个:

class Abstract1(object):
    """Some description that tells you it's abstract,
    often listing the methods you're expected to supply."""
    def aMethod(self):
        raise NotImplementedError( "Should have implemented this" )

因为在Python中没有(也不需要)一个正式的接口协议,类Java的抽象类和接口的区别并不存在。如果有人尝试定义一个正式的接口,它其实也是一个抽象类。唯一的不同就是在文档注释的表述。

并且当你使用鸭子类型时,抽象类和接口的区别有点吹毛求疵了。

Java使用接口是因为它没有多重继承。

因为Python有多重继承,你可能还会看到类似这样的东西:

class SomeAbstraction( object ):
    pass # lots of stuff - but missing something

class Mixin1( object ):
    def something( self ):
        pass # one implementation

class Mixin2( object ):
    def something( self ):
        pass # another

class Concrete1( SomeAbstraction, Mixin1 ):
    pass

class Concrete2( SomeAbstraction, Mixin2 ):
    pass

这是一种使用混合抽象超类去创建不相交的具体子类的方法。

Python 的__slots__

问题链接

引用Jacob Hallen

__slots__的正确使用方法是保存对象的空间。取代使用允许任何时间给类添加属性的动态字典,有一种在创建之后不允许添加的静态结构。使用slots节省了给所有对象同一个字典的系统开销。有时候这是一个很有效的优化,但它也会变得毫无用处,前提是Python的解释器足够动态化,可以在确实需要为对象增加某些东西时只需要字典。

不幸的是,使用slots有一个副作用。他们通过一种方法改变了那些带有slots的对象的表现形式,使它们被古怪的控制者和细小的静态归类滥用。这很糟糕,因为古怪的控制者应该滥用元类,而细小的静态归类应该滥用生成器。但是从Python开始,只有这一种显著的方法了。

将CPython做的很聪明,聪明到可以不用__slots__保存空间,是一个主要的工作,这也就是为什么它不再P3k的更改列表中(到目前为止)。