diff --git a/pykusto/expressions.py b/pykusto/expressions.py
index d921749..e5ceedc 100644
--- a/pykusto/expressions.py
+++ b/pykusto/expressions.py
@@ -14,7 +14,7 @@ MappingType = Union[Mapping, 'MappingExpression']
 DatetimeType = Union[datetime, 'DatetimeExpression']
 TimespanType = Union[timedelta, 'TimespanExpression']
 AggregationType = Union['AggregationExpression']
-DynamicType = Union[ArrayType, MappingType]
+DynamicType = Union['DynamicExpression']
 OrderType = Union[DatetimeType, TimespanType, NumberType, StringType]
 
 
@@ -421,7 +421,20 @@ class TimespanExpression(BaseExpression):
         )))
 
 
-class ArrayExpression(BaseExpression):
+class ArrayBaseExpression(BaseExpression):
+    def __getitem__(self, index: NumberType) -> 'AnyExpression':
+        return AnyExpression(KQL('{}[{}]'.format(self.kql, _subexpr_to_kql(index))))
+
+
+class MappingBaseExpression(BaseExpression):
+    def __getitem__(self, index: StringType) -> 'AnyExpression':
+        return AnyExpression(KQL('{}[{}]'.format(self.kql, _subexpr_to_kql(index))))
+
+    def __getattr__(self, name: str) -> 'AnyExpression':
+        return AnyExpression(KQL('{}.{}'.format(self.kql, name)))
+
+
+class ArrayExpression(ArrayBaseExpression):
     def __len__(self) -> NumberExpression:
         return self.array_length()
 
@@ -437,16 +450,13 @@ class ArrayExpression(BaseExpression):
             ', '.join('{}'.format(_subexpr_to_kql(e) for e in elements))
         )))
 
-    def __getitem__(self, index: NumberType) -> BaseExpression:
-        return BaseExpression(KQL('{}[{}]'.format(self.kql, _subexpr_to_kql(index))))
-
     def assign_to(self, *columns: 'Column') -> 'AssignmentBase':
         if len(columns) <= 1:
             return super().assign_to(*columns)
         return AssignmentToMultipleColumns(columns, self)
 
 
-class MappingExpression(BaseExpression):
+class MappingExpression(MappingBaseExpression):
     def keys(self) -> ArrayExpression:
         return ArrayExpression(KQL('bag_keys({})'.format(self.kql)))
 
@@ -456,8 +466,18 @@ class MappingExpression(BaseExpression):
             ', '.join('"{}", {}'.format(k, _subexpr_to_kql(v)) for k, v in kwargs)
         )))
 
-    def __getitem__(self, index: StringType) -> BaseExpression:
-        return BaseExpression(KQL('{}[{}]'.format(self.kql, _subexpr_to_kql(index))))
+
+class DynamicExpression(ArrayExpression, MappingExpression):
+    def __getitem__(self, index: Union[StringType, NumberType]) -> 'AnyExpression':
+        return AnyExpression(KQL('{}[{}]'.format(self.kql, _subexpr_to_kql(index))))
+
+
+class AnyExpression(
+    NumberExpression, BooleanExpression,
+    StringExpression, DynamicExpression,
+    DatetimeExpression, TimespanExpression
+):
+    pass
 
 
 class AggregationExpression(BaseExpression):
@@ -545,20 +565,13 @@ class AssignmentFromAggregationToColumn(AssignmentBase):
         super().__init__(None if column is None else column.kql, aggregation)
 
 
-class Column(
-    NumberExpression, BooleanExpression, StringExpression,
-    ArrayExpression, MappingExpression, DatetimeExpression,
-    TimespanExpression
-):
+class Column(AnyExpression):
     _name: str
 
     def __init__(self, name: str) -> None:
         super().__init__(KQL("['{}']".format(name) if '.' in name else name))
         self._name = name
 
-    def __getattr__(self, name: str) -> 'Column':
-        return Column(self._name + '.' + name)
-
     def as_subexpression(self) -> KQL:
         return self.kql
 
@@ -572,10 +585,6 @@ class Column(
             return AssignmentFromColumnToColumn(columns[0], self)
         return ArrayExpression.assign_to(self, *columns)
 
-    def __call__(self, *args, **kwargs):
-        # Someone tried to call a non-existent method, and a column object was generated
-        raise AttributeError("No such method: " + self._name.split('.')[-1])
-
 
 class ColumnGenerator:
     def __getattr__(self, name: str) -> Column:
diff --git a/setup.py b/setup.py
index b31e771..01983a5 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
 
 setup(
     name='pykusto',
-    version='0.0.5',
+    version='0.0.6',
     packages=find_packages(exclude=['test']),
     url='https://github.com/Azure/pykusto',
     license='MIT License',

Refactor array, mapping dynamic types
* Introduce DynamicExpression and AnyExpression
* ArrayExpression and MappingExpression should return AnyExpression
* Delete Column.__getitem__
* Fix inheritance tree
diff --git a/test/test_expressions.py b/test/test_expressions.py
index 7c42e8a..5b6f766 100644
--- a/test/test_expressions.py
+++ b/test/test_expressions.py
@@ -38,22 +38,46 @@ class TestExpressions(TestBase):
             Query().where(col.arr[col.foo * 2] == 'bar').render(),
         )
 
+    def test_array_access_yields_any_expression(self):
+        self.assertEqual(
+            ' | where (cos(arr[3])) < 1',
+            Query().where(col.arr[3].cos() < 1).render(),
+        )
+
     def test_mapping_access(self):
         self.assertEqual(
             ' | where (dict["key"]) == "bar"',
             Query().where(col.dict['key'] == 'bar').render(),
         )
 
+    def test_mapping_access_attribute(self):
+        self.assertEqual(
+            ' | where (dict.key) == "bar"',
+            Query().where(col.dict.key == 'bar').render(),
+        )
+
     def test_mapping_access_expression_index(self):
         self.assertEqual(
             ' | where (dict[foo]) == "bar"',
             Query().where(col.dict[col.foo] == 'bar').render(),
         )
 
+    def test_mapping_access_yields_any_expression(self):
+        self.assertEqual(
+            ' | where (dict["key"]) contains "substr"',
+            Query().where(col.dict['key'].contains("substr")).render(),
+        )
+
+    def test_dynamic(self):
+        self.assertEqual(
+            ' | where (dict["foo"][0].bar[1][2][(tolower(bar))]) > time(1.0:0:0.0)',
+            Query().where(col.dict['foo'][0].bar[1][2][col.bar.lower()] > timedelta(1)).render(),
+        )
+
     def test_assign_to(self):
         self.assertEqual(
-            " | extend ['foo.bar'] = (shoo * 2)",
-            Query().extend((col.shoo * 2).assign_to(col.foo.bar)).render(),
+            " | extend bar = (foo * 2)",
+            Query().extend((col.foo * 2).assign_to(col.bar)).render(),
         )
         self.assertEqual(
             " | extend foo = (shoo * 2)",
@@ -78,19 +102,7 @@ class TestExpressions(TestBase):
             col.foo.is_empty().kql,
         )
 
-    def test_method_does_not_exist(self):
-        self.assertRaises(
-            AttributeError("No such method: non_existant_method"),
-            col.foo.non_existant_method,
-        )
-
     def test_column_generator(self):
-        self.assertEqual(
-            " | project ['foo.bar']",
-            Query().project(col.foo.bar).render(),
-        )
-
-    def test_column_generator_2(self):
         self.assertEqual(
             " | project ['foo.bar']",
             Query().project(col['foo.bar']).render(),

pip install -e .
parse_log_pytest
requirements.txt
pytest
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
adal==1.2.7
azure-kusto-data==0.0.36
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
charset-normalizer==3.4.1
cryptography==44.0.2
exceptiongroup==1.2.2
idna==3.10
importlib-metadata==6.7.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.2.0
pycparser==2.21
PyJWT==2.8.0
-e git+https://github.com/Azure/pykusto.git@290b60fd7ed3b61c05a3959c1adc7d4ce943f5db#egg=pykusto
pytest==7.4.4
python-dateutil==2.9.0.post0
requests==2.31.0
six==1.17.0
tomli==2.0.1
typing_extensions==4.7.1
urllib3==2.0.7
zipp==3.15.0

name: pykusto
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - adal==1.2.7
      - azure-kusto-data==0.0.36
      - cffi==1.15.1
      - charset-normalizer==3.4.1
      - cryptography==44.0.2
      - exceptiongroup==1.2.2
      - idna==3.10
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - packaging==24.0
      - pluggy==1.2.0
      - pycparser==2.21
      - pyjwt==2.8.0
      - pytest==7.4.4
      - python-dateutil==2.9.0.post0
      - requests==2.31.0
      - six==1.17.0
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - zipp==3.15.0
prefix: /opt/conda/envs/pykusto

test/test_expressions.py::TestExpressions::test_array_access_yields_any_expression test/test_expressions.py::TestExpressions::test_dynamic test/test_expressions.py::TestExpressions::test_mapping_access_attribute test/test_expressions.py::TestExpressions::test_mapping_access_yields_any_expression
test/test_expressions.py::TestExpressions::test_array_access test/test_expressions.py::TestExpressions::test_array_access_expression_index test/test_expressions.py::TestExpressions::test_assign_to test/test_expressions.py::TestExpressions::test_between_timespan test/test_expressions.py::TestExpressions::test_column_generator test/test_expressions.py::TestExpressions::test_contains test/test_expressions.py::TestExpressions::test_extend_const test/test_expressions.py::TestExpressions::test_has test/test_expressions.py::TestExpressions::test_is_empty test/test_expressions.py::TestExpressions::test_is_in test/test_expressions.py::TestExpressions::test_mapping_access test/test_expressions.py::TestExpressions::test_mapping_access_expression_index test/test_expressions.py::TestExpressions::test_not_contains
setup.py pykusto/expressions.py
setup.py pykusto/expressions.py