Python - динамическая типизация

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Python - динамическая типизация

Непрочитанное сообщение Olej » 25 авг 2013, 15:15

Тема переплыла вот отсюда: Осваиваем Python по-быстрому:
Это (динамическая типизация и её последствия) настолько важная отдельная сторона Python, что её детальное рассмотрение на примерах нужно вынести в отдельную тему обсуждения ...
Есть и ещё одна сторона этого вопроса:
- в чём принципиальная разница между изменяемыми (список, словарь, ...) и неизменяемыми (целое, строка, кортеж, ...) типами данных?
- какие (по какому принципу) те или иные типы данных могут (или не могут) быть ключами словарей?
- как и при каких условиях ключом словаря может быть собственный определённый класс данных?
Но об этом позже...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python - динамическая типизация

Непрочитанное сообщение Olej » 25 авг 2013, 15:27

1. Вот как одна и та же переменная i динамически становится всем чем угодно:

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-

def show():
    print "i - теперь это : %s => %s" % ( type( i ), str( i ) )

i = 1; show()
i = 1.5e-2; show()
i = "теперь это строка"; show()
i = [ 1, 2, 3 ]; show()
i = [ 3 * x for x in range( 3 ) ]; show()   # списковая сборка
i = [ [ x, x**2 ] for x in range( 3 ) ]; show()
i = ( 1, 2, 3 ); show()
i = { 1, 2, 3 }; show()                     # множество
i = { 1:"one", 2:"two", 3:"three" }; show() # словарь
i = lambda x: "фиктивная функция"; show()
i = compile( 'lambda x: "ещё одна фиктивная функция"', '', 'eval' ); show()

Код: Выделить всё

bash-4.2$ ./tp1.py
i - теперь это : <type 'int'> => 1
i - теперь это : <type 'float'> => 0.015
i - теперь это : <type 'str'> => теперь это строка
i - теперь это : <type 'list'> => [1, 2, 3]
i - теперь это : <type 'list'> => [0, 3, 6]
i - теперь это : <type 'list'> => [[0, 0], [1, 1], [2, 4]]
i - теперь это : <type 'tuple'> => (1, 2, 3)
i - теперь это : <type 'set'> => set([1, 2, 3])
i - теперь это : <type 'dict'> => {1: 'one', 2: 'two', 3: 'three'}
i - теперь это : <type 'function'> => <function <lambda> at 0xb765110c>
i - теперь это : <type 'code'> => <code object <module> at 0xb764bba8, file "", line 1>
Вложения
types.tgz
(1.82 КБ) 428 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python - динамическая типизация

Непрочитанное сообщение Olej » 25 авг 2013, 15:34

2. А теперь по-другому (архив тот же):

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-

def show():
    try:
        print "i - это :\t%s\t. id=%08x . hash=%i" % ( type( i ),  id( i ), long( hash( i ) ) )
    except TypeError:
        print "i - это :\t%s\t. id=%08x . не хэшируемый тип!" % ( type( i ), id( i ) )

i = 1; show()
i = 1.5e-2; show()
i = complex( 3.0, 5.0 ); show()
i = "теперь это строка"; show()
i = [ 1, 2, 3 ]; show()
i = [ 3 * x for x in range( 3 ) ]; show()   # списковая сборка
i = [ [ x, x**2 ] for x in range( 3 ) ]; show()
i = ( 1, 2, 3 ); show()
i = { 1, 2, 3 }; show()                     # множество
i = { 1:"one", 2:"two", 3:"three" }; show() # словарь
i = lambda x: "фиктивная функция"; show()
i = compile( 'lambda x: "ещё одна фиктивная функция"', '', 'eval' ); show()
i = iter( '12345' ); show()

Код: Выделить всё

bash-4.2$ ./tp2.py
i - это :	<type 'int'>	. id=08fdf0b0 . hash=1
i - это :	<type 'float'>	. id=08fe8304 . hash=-2061781074
i - это :	<type 'complex'>	. id=b766e9c8 . hash=5000018
i - это :	<type 'str'>	. id=b766d4f0 . hash=917049469
i - это :	<type 'list'>	. id=b76e0f8c . не хэшируемый тип!
i - это :	<type 'list'>	. id=b768276c . не хэшируемый тип!
i - это :	<type 'list'>	. id=b76e0f8c . не хэшируемый тип!
i - это :	<type 'tuple'>	. id=b768157c . hash=-378539185
i - это :	<type 'set'>	. id=b76ccf7c . не хэшируемый тип!
i - это :	<type 'dict'>	. id=b768302c . не хэшируемый тип!
i - это :	<type 'function'>	. id=b766dbc4 . hash=1266052540
i - это :	<type 'code'>	. id=b76755c0 . hash=-2089338066
i - это :	<type 'iterator'>	. id=b768f18c . hash=-881422568
Как я это понимаю - тот тип, для которого не определена hash-функция - это "изменяемый" в терминологии Python тип, а для которого определена - "неизменяемый".
И неизменяемые типы могут быть ключами каких-то словарей, а вот изменяемые - не могут, потому что, будь для них и не возбуждаемо исключение TypeError, их значение хэш-функции (ключа для словаря) могло бы поменяться в любой момент (например, выполнением .append()), и структура словаря разрушилась бы...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python - динамическая типизация

Непрочитанное сообщение Olej » 06 сен 2013, 00:11

Поехали дальше с динамической типизацией и типами данных Python ...
Olej писал(а):Как я это понимаю - тот тип, для которого не определена hash-функция - это "изменяемый" в терминологии Python тип, а для которого определена - "неизменяемый".
И неизменяемые типы могут быть ключами каких-то словарей, а вот изменяемые - не могут, потому что, будь для них и не возбуждаемо исключение TypeError, их значение хэш-функции (ключа для словаря) могло бы поменяться в любой момент (например, выполнением .append()), и структура словаря разрушилась бы...
Кстати, термины "изменяемый" и "неизменяемый", которые кочуют из одного в другой изданных передах по Python, книгах, по моему мнению очень неудачные. В оригинале это (см. документацию) mutable и immutable:
The value of some objects can change. Objects whose value can change are said to be mutable; objects whose value is unchangeable once they are created are called immutable. (The value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.) An object’s mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable.
Терминология "изменяемый" и "неизменяемый" как-то неявно притягивает понятие const ... но это совсем не так: поскольку в Python всё является ссылками, то значения по этим ссылкам могут меняться как угодно, важно чтобы структура контейнера оставалась неизменной: если в кортеже 4 элемента, то их должно и быть 4 на протяжении всего времени жизни кортежа, а если в списке 4 элемента, то через 2 строки их может стать 104.

И всё это непосредственно связано с методом hash() объектов. Для immutable типов значение hash() не может изменяться, и только такие типы могут быть ключом словаря. Элементарно понятно что такое словарь (целочисленные ключи):

Код: Выделить всё

d = { 0:"#1", 1:"#2", 2:"#3" } 
Менее естественный словарь ... но и с этим можно мириться ;-) :

Код: Выделить всё

d = { "one":"#1", "two":"#2", "three":"#3" } 
Но ключами могут быть (при соблюдении некоторых условий) любые сколь угодно сложные объекты собственных классов. При условии, что этих объектов будут фиксированные hash(), т.е. они будут immutable.
Пример ... с которым пришлось повозиться:

Код: Выделить всё

bash-4.2$ cat tp-hash.py 
#!/usr/bin/python
# -*- coding: utf-8 -*-

def show_instances( v ):
    print( "объект: {0} (число таких объектов {1})".format( v, v.numInstances ) )
    try:
        print( "{0}, class: {1}, dictionary: {2}".format( type( v ), v.__class__, v.__dict__ ) )
        print( "id=0x{0:x}, hash={1}".format( id( v ), long( hash( v ) ) ) )
    except TypeError:
        print( "Не хэшируемый объект! : {0}".format( type( v ) ) )

class key0:
    numInstances = 0
    def __init__( self, id ):
        self.id = id
        key0.numInstances = key0.numInstances + 1

class key1( key0 ):
    numInstances = 0
    def __init__( self, id ):
        key0.__init__( self, id )  
        key1.numInstances = key1.numInstances + 1
    def __hash__( self ):
        return self.id * self.id

class key2( key0 ):
    numInstances = 0
    def __init__( self, id ):
        key0.__init__( self, id )  
        key2.numInstances = key2.numInstances + 1
    def __hash__( self ):
        return 123

class key3( key0 ):
    numInstances = 0
    def __init__( self, id ):
        key0.__init__( self, id )  
        key3.numInstances = key3.numInstances + 1
    def __hash__( self ):
        raise TypeError

class key4( key0 ):
    numInstances = 0
    def __init__( self, id ):
        key0.__init__( self, id )  
        key4.numInstances = key4.numInstances + 1
    def __hash__( self ):
        return None

tk = ( key0, key1, key2, key3, key4 )                   # кортеж выбираемых классов
for clas in tk:                                         # выбор класса!
    print( "---------------------------------------------------------" )
    t = [ clas( x ) for x in range( 1, 4 ) ]            # список объектов выбранного класса 
    show_instances( t[ 2 ] )
    try:
        d = { t[ 0 ]:"#1", t[ 1 ]:"#2", t[ 2 ]:"#3" };  # словарь с ключами из классов
        print( d )
        print( d[ t[ 1 ] ] )
    except TypeError:
        print( "Не может использоваться как ключ словаря!" )
    print( "---------------------------------------------------------" )
Здесь 5 классов (key0, key1, key2, key3, key4 ) из которых первые 3 могут быть ключами словаря, а последние 2 - не могут.
Там есть попутно интересные "мелочи" ;-) :
- как tk строится как кортеж (можно список) классов (не объектов, а классов) ... - классы такие же строительные элементы как переменные, объекты, или функции...
- t по переменной цикла изменяет класс своих объектов ...
- даже если __hash__( self ) возвращает одинаковое значение для любых объектов класса (класс key2), то в словарь с такими ключами строится правильно, т.е. словарь как хэш-таблица строится не непосредственно из значений hash(), а какой-то их модификации
- достаточно чтобы метод __hash__( self ) просто возвращал None (класс key4) чтобы класс стал immutable

Вывод этого примера не форматированный толком, но очень поучительный:

Код: Выделить всё

bash-4.2$ ./tp-hash.py
---------------------------------------------------------
объект: <__main__.key0 instance at 0xb76ac22c> (число таких объектов 3)
<type 'instance'>, class: __main__.key0, dictionary: {'id': 3}
id=0xb76ac22c, hash=-881415134
{<__main__.key0 instance at 0xb76ac20c>: '#2', <__main__.key0 instance at 0xb76ac22c>: '#3', <__main__.key0 instance at 0xb76ac1ec>: '#1'}
#2
---------------------------------------------------------
---------------------------------------------------------
объект: <__main__.key1 instance at 0xb76ac2ec> (число таких объектов 3)
<type 'instance'>, class: __main__.key1, dictionary: {'id': 3}
id=0xb76ac2ec, hash=9
{<__main__.key1 instance at 0xb76ac2ac>: '#1', <__main__.key1 instance at 0xb76ac2cc>: '#2', <__main__.key1 instance at 0xb76ac2ec>: '#3'}
#2
---------------------------------------------------------
---------------------------------------------------------
объект: <__main__.key2 instance at 0xb76ac30c> (число таких объектов 3)
<type 'instance'>, class: __main__.key2, dictionary: {'id': 3}
id=0xb76ac30c, hash=123
{<__main__.key2 instance at 0xb76ac20c>: '#2', <__main__.key2 instance at 0xb76ac30c>: '#3', <__main__.key2 instance at 0xb76ac22c>: '#1'}
#2
---------------------------------------------------------
---------------------------------------------------------
объект: <__main__.key3 instance at 0xb76ac28c> (число таких объектов 3)
<type 'instance'>, class: __main__.key3, dictionary: {'id': 3}
Не хэшируемый объект! : <type 'instance'>
Не может использоваться как ключ словаря!
---------------------------------------------------------
---------------------------------------------------------
объект: <__main__.key4 instance at 0xb76ac3ac> (число таких объектов 3)
<type 'instance'>, class: __main__.key4, dictionary: {'id': 3}
Не хэшируемый объект! : <type 'instance'>
Не может использоваться как ключ словаря!
---------------------------------------------------------
Вложения
types.tgz
(3.91 КБ) 405 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python - динамическая типизация

Непрочитанное сообщение Olej » 06 сен 2013, 00:26

Olej писал(а):Поехали дальше с динамической типизацией и типами данных Python ...
И ещё один пример касательно типов ... относительно равенства-неравенства объектов (а также <, <=, >, >=).
См. опять же ту же страницу документации
То, что в Python всё есть ссылки, и тогда == в простейшем виде это только тождественность (совпадение адреса размещения) - об этом много написано.
Но это можно всё поменять (пример в том же архиве, что выше в сообщении):

Код: Выделить всё

bash-4.2$ cat  tp-eq.py 
#!/usr/bin/python
# -*- coding: utf-8 -*-

def compare( v1, v2 ):
    try:
        print( "сравнения объекта <{0}({1})> и объекта <{2}({3})>\t: '=='->{4}\t'!='->{5}" \
               .format( v1.__class__, str( v1 ), v2.__class__, str( v2 ), v1==v2, v1!=v2 ) )
    except TypeError:
        print( "сравнения объекта <{0}({1})> и объекта <{2}({3})>\t: не сравнимые типы объектов!" \
               .format( v1.__class__, str( v1 ), v2.__class__, str( v2 ) ) )

class obj0:
    def __init__( self, id ):
        self.id = id
    def __str__( self ):
        return str( self.id )

class obj1( obj0 ):
    def __init__( self, id ):
        obj0.__init__( self, id )  
    def __eq__( self, other ):
        if isinstance( other, obj1 ): 
            return self.id == other.id 
        else:
            raise TypeError

class obj2( obj0 ):
    def __init__( self, id ):
        obj0.__init__( self, id )  
    def __eq__( self, other ):
        if isinstance( other, obj2 ): 
            return self.id == other.id 
        else:
            raise TypeError
    def __ne__( self, other ):
        return not self == other 


compare( obj0( 13 ), obj0( 13 ) )
compare( obj0( 13 ), obj0( 31 ) )
compare( obj1( 24 ), obj1( 24 ) )
compare( obj1( 24 ), obj1( 42 ) )
compare( obj2( 35 ), obj2( 35 ) )
compare( obj2( 35 ), obj2( 53 ) )

compare( obj0( 77 ), obj1( 77 ) )
compare( obj1( 88 ), obj0( 88 ) )
compare( 55, obj1( 55 ) )
compare( obj1( 55 ), 55 )
Объекты класса obj0 сравниваются традиционно, поэтому : obj0( 13 ) == obj0( 13 ) даёт False.
Объекты класса obj1 сравниваются на == по значению поля id (параметру конструктора при создании), поэтому obj1( 24 ) == obj1( 24 ) будет True.
Но удобная нам реализация == совершенно не влечёт за собой автоматом эквивалентную реализацию и для != (а также <, <=, >, >=), и поэтому obj1( 24 ) != obj1( 24 ) будет упорно True ... т.е. эти объекты одновременно как равны так и не равны ;-) .
И только в классе obj2 операции == и != сделаны симметричными.

Код: Выделить всё

bash-4.2$ ./tp-eq.py
сравнения объекта <__main__.obj0(13)> и объекта <__main__.obj0(13)>     : '=='->False   '!='->True
сравнения объекта <__main__.obj0(13)> и объекта <__main__.obj0(31)>     : '=='->False   '!='->True
сравнения объекта <__main__.obj1(24)> и объекта <__main__.obj1(24)>     : '=='->True    '!='->True
сравнения объекта <__main__.obj1(24)> и объекта <__main__.obj1(42)>     : '=='->False   '!='->True
сравнения объекта <__main__.obj2(35)> и объекта <__main__.obj2(35)>     : '=='->True    '!='->False
сравнения объекта <__main__.obj2(35)> и объекта <__main__.obj2(53)>     : '=='->False   '!='->True
сравнения объекта <__main__.obj0(77)> и объекта <__main__.obj1(77)>     : не сравнимые типы объектов!
сравнения объекта <__main__.obj1(88)> и объекта <__main__.obj0(88)>     : не сравнимые типы объектов!
сравнения объекта <<type 'int'>(55)> и объекта <__main__.obj1(55)>      : не сравнимые типы объектов!
сравнения объекта <__main__.obj1(55)> и объекта <<type 'int'>(55)>      : не сравнимые типы объектов!

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python - динамическая типизация

Непрочитанное сообщение Olej » 06 сен 2013, 14:33

Переписал все предыдущие примеры так, чтобы один и тот же файл кода мог исполняться и в Python 2 и в Python 3, для сравнения, для типизации это особенно интересно.
Для чего в терминальных программах достаточно переписать операторы print в функциональные вызовы print(), что-то типа такого, чтобы вместо такого:

Код: Выделить всё

print "i - это : %s . id=%08x . hash=%i" % ( type( i ),  id( i ), long( hash( i ) ) )


теперь бы стало что-то такое:

Код: Выделить всё

print( "i - это : {0:s} . id={1:08x} . hash={2:d}".format( ( type( i ),  id( i ), hash( i ) )


Но повозиться с форматами, чтобы 2/3 выглядели адекватно, сравнимо - пришлось повозиться (форматирование срабатывает по-разному, особенно UTF-строки, с которыми в Python 2, как уже отмечали, негаразды...). Но возня стоила того, потому как посмотреть результаты интересно... Это то, как переменная i пробегая самые разные значения меняет тип, как этот тип идентифицируется Python и т.д. (в листингах: сначала тип, потом id(), затем hash(), где он может вычисляться и ==> str(), то есть значение):

Код: Выделить всё

bash-4.2$ ./tp2.py
i - это : <type 'int'>            | id=009c290b0 | hash=000000000001 ==> 1
i - это : <type 'float'>          | id=009c32304 | hash=-02061781074 ==> 0.015
i - это : <type 'complex'>        | id=0b76989c8 | hash=-00345735165 ==> (3+5.5j)
i - это : <type 'str'>            | id=0b769c360 | hash=000132808738 ==> теперь это UNICODE строка
i - это : <type 'list'>           | id=0b770af8c | не хэшируемый тип! ==> [1, 2, 3]
i - это : <type 'list'>           | id=0b76acd0c | не хэшируемый тип! ==> [0, 3, 6]
i - это : <type 'list'>           | id=0b770af8c | не хэшируемый тип! ==> [[0, 0], [1, 1], [2, 4]]
i - это : <type 'tuple'>          | id=0b76ab75c | hash=-00378539185 ==> (1, 2, 3)
i - это : <type 'set'>            | id=0b76f6f7c | не хэшируемый тип! ==> set([1, 2, 3])
i - это : <type 'dict'>           | id=0b76ad79c | не хэшируемый тип! ==> {1: 'one', 2: 'two', 3: 'three'}
i - это : <type 'function'>       | id=0b76a510c | hash=-00881416944 ==> <function <lambda> at 0xb76a510c>
i - это : <type 'code'>           | id=0b76a78d8 | hash=-02089338066 ==> <code object <module> at 0xb76a78d8, file "", line 1>
i - это : <type 'iterator'>       | id=0b76b854c | hash=-00881412012 ==> <iterator object at 0xb76b854c>
i - это : <type 'instance'>       | id=0b76b84cc | hash=-00881412020 ==> <__main__.own1 instance at 0xb76b84cc>
i - это : <type 'classobj'>       | id=0b76a026c | hash=-00881418202 ==> __main__.own1
i - это : <type 'instance'>       | id=0b76b856c | не хэшируемый тип! ==> <__main__.own2 instance at 0xb76b856c>
i - это : <type 'classobj'>       | id=0b76a020c | hash=-00881418208 ==> __main__.own2

Код: Выделить всё

bash-4.2$ python3 tp2.py
i - это : <class 'int'>           | id=0475b1580 | hash=000000000001 ==> 1
i - это : <class 'float'>         | id=008f8e704 | hash=001578400478 ==> 0.015
i - это : <class 'complex'>       | id=0b737a200 | hash=-01068741806 ==> (3+5.5j)
i - это : <class 'str'>           | id=0b73daf60 | hash=001282498349 ==> теперь это UNICODE строка
i - это : <class 'list'>          | id=0b7412c6c | не хэшируемый тип! ==> [1, 2, 3]
i - это : <class 'list'>          | id=0b741274c | не хэшируемый тип! ==> [0, 3, 6]
i - это : <class 'list'>          | id=0b7412c6c | не хэшируемый тип! ==> [[0, 0], [1, 1], [2, 4]]
i - это : <class 'tuple'>         | id=0b7411194 | hash=-00378539185 ==> (1, 2, 3)
i - это : <class 'set'>           | id=0b7407b1c | не хэшируемый тип! ==> {1, 2, 3}
i - это : <class 'dict'>          | id=0b74a2bdc | не хэшируемый тип! ==> {1: 'one', 2: 'two', 3: 'three'}
i - это : <class 'function'>      | id=0b73c926c | hash=-00881604314 ==> <function <lambda> at 0xb73c926c>
i - это : <class 'code'>          | id=0b73cbb10 | hash=-00858576570 ==> <code object <module> at 0xb73cbb10, file "", line 1>
i - это : <class 'str_iterator'>  | id=0b73d546c | hash=-00881601210 ==> <str_iterator object at 0xb73d546c>
i - это : <class '__main__.own1'> | id=0b73d54ec | hash=-00881601202 ==> <__main__.own1 object at 0xb73d54ec>
i - это : <class 'type'>          | id=009083e04 | hash=001083212768 ==> <class '__main__.own1'>
i - это : <class '__main__.own2'> | id=0b73d550c | не хэшируемый тип! ==> <__main__.own2 object at 0xb73d550c>
i - это : <class 'type'>          | id=009081d1c | hash=-01064271407 ==> <class '__main__.own2'>
Видно, что теперь (Python 3), как и обещано в описаниях, любая переменная, будь то элементарный int - это экземпляр класса ... класса int ... или класса float.
(вообще то, это может сильно замедлять выполнение, ... что, похоже, и наблюдалось при тестировании параллельных ветвей выполнения ... рядом тема).

P.S. Ну и в примерах несколько расширен набор тех типов, которые может принимать переменная.

2-й интересный пример - это с операторами == для != экземляров классов... :

Код: Выделить всё

bash-4.2$ ./tp-eq.py
сравнения объекта <__main__.obj0(13)> и объекта <__main__.obj0(13)>     : '=='->False   |   !='->True
сравнения объекта <__main__.obj0(13)> и объекта <__main__.obj0(31)>     : '=='->False   |   !='->True
сравнения объекта <__main__.obj1(24)> и объекта <__main__.obj1(24)>     : '=='->True    |   !='->True
сравнения объекта <__main__.obj1(24)> и объекта <__main__.obj1(42)>     : '=='->False   |   !='->True
сравнения объекта <__main__.obj2(35)> и объекта <__main__.obj2(35)>     : '=='->True    |   !='->False
сравнения объекта <__main__.obj2(35)> и объекта <__main__.obj2(53)>     : '=='->False   |   !='->True
сравнения объекта <__main__.obj0(77)> и объекта <__main__.obj1(77)>     : не сравнимые типы объектов!
сравнения объекта <__main__.obj1(88)> и объекта <__main__.obj0(88)>     : не сравнимые типы объектов!
сравнения объекта <<type 'int'>(55)> и объекта <__main__.obj1(55)>      : не сравнимые типы объектов!
сравнения объекта <__main__.obj1(55)> и объекта <<type 'int'>(55)>      : не сравнимые типы объектов!

Код: Выделить всё

bash-4.2$ python3 tp-eq.py
сравнения объекта <<class '__main__.obj0'>(13)> и объекта <<class '__main__.obj0'>(13)> : '=='->False   |   !='->True
сравнения объекта <<class '__main__.obj0'>(13)> и объекта <<class '__main__.obj0'>(31)> : '=='->False   |   !='->True
сравнения объекта <<class '__main__.obj1'>(24)> и объекта <<class '__main__.obj1'>(24)> : '=='->True    |   !='->False
сравнения объекта <<class '__main__.obj1'>(24)> и объекта <<class '__main__.obj1'>(42)> : '=='->False   |   !='->True
сравнения объекта <<class '__main__.obj2'>(35)> и объекта <<class '__main__.obj2'>(35)> : '=='->True    |   !='->False
сравнения объекта <<class '__main__.obj2'>(35)> и объекта <<class '__main__.obj2'>(53)> : '=='->False   |   !='->True
сравнения объекта <<class '__main__.obj0'>(77)> и объекта <<class '__main__.obj1'>(77)> : не сравнимые типы объектов!
сравнения объекта <<class '__main__.obj1'>(88)> и объекта <<class '__main__.obj0'>(88)> : не сравнимые типы объектов!
сравнения объекта <<class 'int'>(55)> и объекта <<class '__main__.obj1'>(55)>           : не сравнимые типы объектов!
сравнения объекта <<class '__main__.obj1'>(55)> и объекта <<class 'int'>(55)>           : не сравнимые типы объектов!
Здесь всё грустнее...
В Python 3 (файл исполняемый один!), похоже, реализация == влечёт за собой адекватное поведение симметричной операции != ... что совсем не так в Python 2 (3-и строки листингов).
И как следствие таких вот "мелочей" (а их, похоже, по-мелочам выявляется весьма много) всё программное обеспечение, написанное под Python 2, будет работать не так под Python 3. Именно не сваливаться синтаксически (это как-раз удачный случай, значит вам повезло ;-) ), а именно выполняться, но результат выдавать не тот, то есть ошибочный, создавая при этом видимость правильной работы.

P.S. Остальные там примеры тоже интересные, и тоже запускаются все как в Python 2, так и в Puthon 3, для сравнения ... но это уже любопытствующие посмотрят сами ... + как всегда, в архиве - текстовый файл .hist, история листингов в хронологии их изменений.
Вложения
types.tgz
(5.72 КБ) 425 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python - динамическая типизация

Непрочитанное сообщение Olej » 08 сен 2013, 00:44

Olej писал(а): Здесь всё грустнее...
В Python 3 (файл исполняемый один!), похоже, реализация == влечёт за собой адекватное поведение симметричной операции != ... что совсем не так в Python 2 (3-и строки листингов).
И как следствие таких вот "мелочей" (а их, похоже, по-мелочам выявляется весьма много) всё программное обеспечение, написанное под Python 2, будет работать не так под Python 3. Именно не сваливаться синтаксически (это как-раз удачный случай, значит вам повезло ;-) ), а именно выполняться, но результат выдавать не тот, то есть ошибочный, создавая при этом видимость правильной работы.
Это ещё один интереснейший вопрос: различия в Python 2 Python 3 :
- синтаксические ... ну это попроще: получил синтаксическую ошибку и исправил ;-)
- динамические - где можно ожидать по-разному поведения одного и того же кода...
- и как это будет выглядеть.

Об этом было уже сказано, и неоднократно:
Скорее всего, у вас не получится полностью отказаться от Python 2, так или иначе вам придется с ним сталкиваться. Однако, это вовсе не значит, что стоит отказываться от Python 3, кардинальных отличий между этими версиями не так уж и много, а основная проблема совместимости заключается именно в модулях.

По этому, я бы рекомендовал учить обе версии. Начните, к примеру, с Python 3, а затем изучите отличия версий. Когда начнется массовый переход на Python 3, уверен, вам пригодятся знания обоих версий.
Пора уже с этим разобраться! ;-)

Новую тему по этому поводу не хочется создавать, т.к. это вопросы достаточно близкие и к текущей теме.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python - динамическая типизация

Непрочитанное сообщение Olej » 08 сен 2013, 01:02

Olej писал(а): Пора уже с этим разобраться! ;-)
Вот такое приложение, которое проверяет разное исполнение одних и тех же конструкций... это его текущий вид, но оно сделано так, что его можно легко дополнять любыми проверяемыми конструкциями, и по ходу я буду показывать обновляемые результаты:

Код: Выделить всё

# -*- coding: utf-8 -*-

prgs = '', '10 / 3', '10 / 3.', '10 / float( 3 )', '', \
       'len( "строка UTF-8" )', '', \
       'type( range( 5 ) )', 'range( 5 )', 'list( range( 5 ) )', \
       'type( xrange( 5 ) )', \

# для UTF-8 строки len() - это число число: Python 2.7.3 - байт, Python 3.2.3 - символов 

n = 1
for x in prgs:
    if len( x ) == 0:
        print( '{:02d}: -------------------------------------'.format( n ) )
        n += 1
    else:
        try:
            print( '{} = {}'.format( x, eval( x ) ) )
        except NameError as err:
            print( '{} => {} !'.format( x, err ) )
        except:
            print( '{} => непонятное исключение !'.format( x, eval( x ) ) )
Python 2:

Код: Выделить всё

bash-4.2$ python 3vs2.py
01: -------------------------------------
10 / 3 = 3
10 / 3. = 3.33333333333
10 / float( 3 ) = 3.33333333333
02: -------------------------------------
len( "строка UTF-8" ) = 18
03: -------------------------------------
type( range( 5 ) ) = <type 'list'>
range( 5 ) = [0, 1, 2, 3, 4]
list( range( 5 ) ) = [0, 1, 2, 3, 4]
type( xrange( 5 ) ) = <type 'xrange'>
Python 3:

Код: Выделить всё

bash-4.2$ python3 3vs2.py
01: -------------------------------------
10 / 3 = 3.3333333333333335
10 / 3. = 3.3333333333333335
10 / float( 3 ) = 3.3333333333333335
02: -------------------------------------
len( "строка UTF-8" ) = 12
03: -------------------------------------
type( range( 5 ) ) = <class 'range'>
range( 5 ) = range(0, 5)
list( range( 5 ) ) = [0, 1, 2, 3, 4]
type( xrange( 5 ) ) => name 'xrange' is not defined !
Результаты, как видите, существенно различные (выполняется один и тот же файл .py - это обязательное условие).
Разные "случаи" разделены, в меру возможностей, по группам операторов (цифра перед разделителем "---- ...."):

1. числовые вычисления:
- операция / в v2 даёт целочисленный результат, а в v3 - вещественный;
- разная точность представления (округления?) вещественных;

2. UTF-8 строки (русскоязычные):
- функция len() v2 подсчитывает байты, а в v3 - символы;

3. типы, функции:
- только в v2 range() возвращает действительно список ... в v3 это особый тип class 'range'...
- который работает как итератор
- если в v3 нужно получить такой список, то используем list( range() ) (преобразование)
- итератор v3 это то же, что очень модный у пользователей v2 xrange() ... (тип type 'xrange')
- но в v3 xrange() просто нет, и вызовет возбуждение исключения NameError
- ну и то здесь видно, что уже говорилось: в v3 - любой тип есть класс, даже простейший int
Вложения
3vs2.tgz
(820 байт) 412 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python - динамическая типизация

Непрочитанное сообщение Olej » 08 сен 2013, 01:10

Olej писал(а): Это ещё один интереснейший вопрос: различия в Python 2 Python 3 :
В документации есть на этот счёт отдельный документ: What’s New in Python.
Но здесь:

1. Все новшества: нововведения, дополнения, изменения - от версии к версии.
Но меня в этом разговоре интересует только что изменилось по толкованию?
(например, нет в v3 оператора print - ну и ладно, он не станет себя вести по-другому)

2. Это только формулировки, и хотелось бы видеть как это будет выглядеть "в натуре" - перевести такие конструкции в фрагменты кода, и "на пальцах" увидеть различия в поведении.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python - динамическая типизация

Непрочитанное сообщение Olej » 08 сен 2013, 17:38

Отличий много, и их всё пребывает, некоторые отличия очень тонкие.
Т.к. их скоро станет до невозможности много (до "немагу" ;-) ), то пришлось перекроить саму программу:
- все отличия поделены на группы (это и раньше было), группы отделены ----------------- и с номером
- теперь параметрами запуска можно указать номер только той группы, которую нужно показать ... или номера таких групп, или диапазон номеров...
- например так:

Код: Выделить всё

$ python 3vs2.py 1,2
...
$ python 3vs2.py 1 2
...
$ python 3vs2.py 2-4
...
$ python 3vs2.py 4 1-3
...
- добавлен вывод (в начальной строке) версии интерпретатора CPython и платформы, на которых это выполнялось, так как их (версию и платформу) понимает сам Python.

Ну и вот ... там есть очень любопытные вещи, см. ;-) :

Код: Выделить всё

bash-4.2$ python 3vs2.py
версия Python 2.7.3 на платформе linux2
01: -------------------------------------
10 / 3 = 3
10 / 3. = 3.33333333333
10 / float( 3 ) = 3.33333333333
02: -------------------------------------
len( "строка UTF-8" ) = 18
03: -------------------------------------
type( range( 5 ) ) = <type 'list'>
range( 5 ) = [0, 1, 2, 3, 4]
list( range( 5 ) ) = [0, 1, 2, 3, 4]
type( xrange( 5 ) ) = <type 'xrange'>
map( int, [ "1", "2", "3" ] ) = [1, 2, 3]
list( map( int, [ "1", "2", "3" ] ) ) = [1, 2, 3]
04: -------------------------------------
reduce( lambda x, y: x + y, range( 5 ) ) = 10

Код: Выделить всё

bash-4.2$ python3 3vs2.py
версия Python 3.2.3 на платформе linux2
01: -------------------------------------
10 / 3 = 3.3333333333333335
10 / 3. = 3.3333333333333335
10 / float( 3 ) = 3.3333333333333335
02: -------------------------------------
len( "строка UTF-8" ) = 12
03: -------------------------------------
type( range( 5 ) ) = <class 'range'>
range( 5 ) = range(0, 5)
list( range( 5 ) ) = [0, 1, 2, 3, 4]
type( xrange( 5 ) ) => name 'xrange' is not defined !
map( int, [ "1", "2", "3" ] ) = <map object at 0xb7418eac>
list( map( int, [ "1", "2", "3" ] ) ) = [1, 2, 3]
04: -------------------------------------
reduce( lambda x, y: x + y, range( 5 ) ) => name 'reduce' is not defined !

Код: Выделить всё

bash-4.2$ python 3vs2.py 1,4
версия Python 2.7.3 на платформе linux2
01: -------------------------------------
10 / 3 = 3
10 / 3. = 3.33333333333
10 / float( 3 ) = 3.33333333333
04: -------------------------------------
reduce( lambda x, y: x + y, range( 5 ) ) = 10

Код: Выделить всё

bash-4.2$ python 3vs2.py 2-4
версия Python 2.7.3 на платформе linux2
02: -------------------------------------
len( "строка UTF-8" ) = 18
03: -------------------------------------
type( range( 5 ) ) = <type 'list'>
range( 5 ) = [0, 1, 2, 3, 4]
list( range( 5 ) ) = [0, 1, 2, 3, 4]
type( xrange( 5 ) ) = <type 'xrange'>
map( int, [ "1", "2", "3" ] ) = [1, 2, 3]
list( map( int, [ "1", "2", "3" ] ) ) = [1, 2, 3]
04: -------------------------------------
reduce( lambda x, y: x + y, range( 5 ) ) = 10

Код: Выделить всё

bash-4.2$ python3 3vs2.py 4,2
версия Python 3.2.3 на платформе linux2
02: -------------------------------------
len( "строка UTF-8" ) = 12
04: -------------------------------------
reduce( lambda x, y: x + y, range( 5 ) ) => name 'reduce' is not defined !
Вложения
3vs2.py
(2.98 КБ) 416 скачиваний

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 8 гостей